明示的な(ブール)のユースケースは何ですか


24

C ++ 20 は、コンストラクターが明示的にされているかどうかをコンパイル時に条件付きで選択する明示的な(ブール値)を導入しました。

以下は私がここで見つけた例です

struct foo {

  // Specify non-integral types (strings, floats, etc.) require explicit construction.

  template <typename T>

  explicit(!std::is_integral_v<T>) foo(T) {}

};

foo a = 123; // OK

foo b = "123"; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)

foo c {"123"}; // OK

誰かexplicit (bool)が使用する以外の他のユースケースを教えてもらえますstd::is_integralか?


1
1つの例はtuple、この機能を使用する場合のように、条件付きで明示的なコンストラクターを実装する方がはるかに簡単になることです。
プレトリアン

1
正解ではありませんが、それを紹介した紙の動機を見ることができます:wg21.link/p0892
N. Shead

例:(コンセプトと一緒に)それ3から0に条件付きで提供される条件付きの明示的なコピーコンストラクタを実装する基底クラスの必要な数のダウンカット
LF

回答:


21

動機自体は論文で見ることができます。

コンストラクターを条件付きで明示的にする必要があります。つまり、以下が必要です。

pair<string, string> safe() {
    return {"meow", "purr"}; // ok
}

pair<vector<int>, vector<int>> unsafe() {
    return {11, 22}; // error
}

前者は問題なく、これらのコンストラクターは暗黙的です。しかし、後者は悪いでしょう、それらのコンストラクタはそうexplicitです。C ++ 17(または概念付きのC ++ 20)では、これを機能させる唯一の方法は、2つのコンストラクター(1つはexplicitです。

template <typename T1, typename T2>
struct pair {
    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2> &&
            std::is_convertible_v<U1, T1> &&
            std::is_convertible_v<U2, T2>
        , int> = 0>
    constexpr pair(U1&&, U2&& );

    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2> &&
            !(std::is_convertible_v<U1, T1> &&
              std::is_convertible_v<U2, T2>)
        , int> = 0>
    explicit constexpr pair(U1&&, U2&& );    
};  

これらはほぼ完全に複製されており、これらのコンストラクターの定義は同じです。

を使用するexplicit(bool)と、単一のコンストラクタを作成できます-構成の条件付きで明示的な部分をexplicit-specifier だけにローカライズします。

template <typename T1, typename T2>
struct pair {
    template <typename U1=T1, typename U2=T2,
        std::enable_if_t<
            std::is_constructible_v<T1, U1> &&
            std::is_constructible_v<T2, U2>
        , int> = 0>
    explicit(!std::is_convertible_v<U1, T1> ||
        !std::is_convertible_v<U2, T2>)
    constexpr pair(U1&&, U2&& );   
};

これは、意図とよりよく一致し、書き込むコードがはるかに少なく、オーバーロードの解決中にコンパイラーが行う作業が少なくなります(選択する必要があるコンストラクターが少ないため)。


1
C ++ 20は、enable_if_t概念を使用して、部品をよりきれいでより単純な制約に変更する機能も提供します。しかし、それはこの質問の要点ではありません。
aschepler

2

私が見るもう1つの可能な使用法は、可変テンプレートでの使用です。

一般に、デフォルトでは、 explicit(変換が必要な場合を除いて)引数が1つだけのコンストラクターを使用をお勧めします。

そう

struct Foo
{
    template <typename ... Ts>
    explicit(sizeof...(Ts) == 1) Foo(Ts&&...);

    // ...
};

0

explicit入力がビューのようなタイプ(生のポインターstd::string_view)であり、呼び出し後に新しいオブジェクトが保持する場合に条件付きで要求する使用例を見ることができます(ビューをコピーするだけで、参照するものはコピーせず、依存し続けます)表示されたオブジェクトの存続期間)、または値のようなタイプの場合があります(コピーの所有権を取得し、外部の存続期間に依存しません)。

そのような状況では、呼び出し元は表示されたオブジェクトを存続させる責任があります(呼び出し先は元のオブジェクトではなくビューを所有しています)。暗黙的に作成されたオブジェクトでは変換が非常に簡単になるため、変換は暗黙的に行われるべきではありません。表示するオブジェクトよりも長生きします。対照的に、値型の場合、新しいオブジェクトは独自のコピーを受け取るため、コピーはコストがかかる可能性がありますが、暗黙的な変換が発生しもコードが間違ってしまうことはありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.