3355

explicitC++ でのキーワードの意味は何ですか?

4

10 に答える 10

3824

コンパイラは、パラメーターを関数に解決するために 1 つの暗黙的な変換を行うことができます。これが意味することは、コンパイラは、単一のパラメーターで呼び出し可能なコンストラクターを使用して、ある型から別の型に変換して、パラメーターの適切な型を取得できるということです。

暗黙的な変換に使用できるコンストラクターを持つクラスの例を次に示します。

class Foo
{
private:
  int m_foo;

public:
  // single parameter constructor, can be used as an implicit conversion
  Foo (int foo) : m_foo (foo) {}

  int GetFoo () { return m_foo; }
};

Fooオブジェクトを受け取る単純な関数を次に示します。

void DoBar (Foo foo)
{
  int i = foo.GetFoo ();
}

DoBar関数が呼び出される場所は次のとおりです。

int main ()
{
  DoBar (42);
}

引数はFooオブジェクトではなく、int. ただし、 を受け取るコンストラクターが存在するFooためint、このコンストラクターを使用してパラメーターを正しい型に変換できます。

コンパイラーは、パラメーターごとにこれを 1 回行うことができます。

コンストラクターの前にキーワードを付けるとexplicit、コンパイラーはそのコンストラクターを暗黙的な変換に使用できなくなります。上記のクラスに追加すると、関数呼び出しでコンパイラ エラーが発生しますDoBar (42)。明示的に変換を呼び出す必要があります DoBar (Foo (42))

これを行う理由は、バグを隠す可能性のある偶発的な構築を避けるためです。
考案された例:

  • MyString指定されたサイズの文字列を構築するコンストラクタを持つクラスがあります。関数print(const MyString&)(およびオーバーロードprint (char *string))があり、 (実際に呼び出すつもりだったprint(3)ときに)呼び出します。「3」を出力することを期待していますが、代わりに長さ 3 の空の文字列を出力します。print("3")
于 2008-09-23T13:59:04.413 に答える
1246

クラスがあるとしますString

class String {
public:
    String(int n); // allocate n bytes to the String object
    String(const char *p); // initializes object with char *p
};

今、試してみると:

String mystring = 'x';

文字'x'は暗黙的に変換されintString(int)コンストラクターが呼び出されます。しかし、これはユーザーが意図したものではありません。したがって、そのような状態を防ぐために、コンストラクターを次のように定義しますexplicit

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};
于 2008-09-23T14:09:06.917 に答える
176

C++ では、必須パラメーターが 1 つだけのコンストラクターは、暗黙的な変換関数と見なされます。パラメータの型をクラスの型に変換します。これが良いことかどうかは、コンストラクターのセマンティクスに依存します。

たとえば、コンストラクターを持つ文字列クラスがある場合、それはString(const char* s)おそらくまさにあなたが望むものです。const char*を期待する関数に a を渡すことができString、コンパイラは自動的に一時Stringオブジェクトを構築します。

一方、コンストラクターがバッファーのサイズをバイト単位で受け取るバッファー クラスがある場合は、コンパイラーが静かにs を s に変換Buffer(int size)することを望まないでしょう。これを防ぐには、次のキーワードを使用してコンストラクターを宣言します。intBufferexplicit

class Buffer { explicit Buffer(int size); ... }

そうすれば、

void useBuffer(Buffer& buf);
useBuffer(4);

コンパイル時エラーになります。一時オブジェクトを渡したい場合Bufferは、明示的に行う必要があります。

useBuffer(Buffer(4));

explicit要約すると、単一パラメーターのコンストラクターがパラメーターをクラスのオブジェクトに変換する場合、おそらくキーワードを使用したくないでしょう。ただし、たまたま単一のパラメーターを受け取るコンストラクターがある場合はexplicit、コンパイラーが予期しない変換で驚かないように、コンストラクターを宣言する必要があります。

于 2008-09-23T16:37:12.770 に答える
56

キーワードexplicitには、

  • 最初の (任意の唯一の) パラメータを型 X に暗黙的に変換するために使用できないクラス X のコンストラクタ

C++ [class.conv.ctor]

1) function-specifier explicit なしで宣言されたコンストラクターは、そのパラメーターの型からそのクラスの型への変換を指定します。このようなコンストラクターは、変換コンストラクターと呼ばれます。

2) 明示的なコンストラクターは、非明示的なコンストラクターと同様にオブジェクトを構築しますが、直接初期化構文 (8.5) またはキャスト (5.2.9、5.4) が明示的に使用されている場合にのみ構築します。デフォルトのコンストラクターは、明示的なコンストラクターの場合があります。そのようなコンストラクターは、デフォルトの初期化または値の初期化 (8.5) を実行するために使用されます。

  • または、直接の初期化と明示的な変換に対してのみ考慮される変換関数。

C++ [class.conv.fct]

2) 変換関数は明示的 (7.1.2) である場合があり、その場合、直接初期化 (8.5) のユーザー定義の変換としてのみ考慮されます。それ以外の場合、ユーザー定義の変換は、割り当てと初期化での使用に制限されません。

概要

明示的な変換関数とコンストラクターは、明示的な変換 (直接初期化または明示的なキャスト操作) にのみ使用できますが、非明示的なコンストラクターと変換関数は、明示的な変換だけでなく暗黙的な変換にも使用できます。

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

構造体X, Y, Zと関数を使用した例foo, bar, baz:

explicit構造体と関数の小さなセットアップを見て、変換と非変換の違いを見てみましょうexplicit

struct Z { };

struct X { 
  explicit X(int a); // X can be constructed from int explicitly
  explicit operator Z (); // X can be converted to Z explicitly
};

struct Y{
  Y(int a); // int can be implicitly converted to Y
  operator Z (); // Y can be implicitly converted to Z
};

void foo(X x) { }
void bar(Y y) { }
void baz(Z z) { }

コンストラクタに関する例:

関数の引数の変換:

foo(2);                     // error: no implicit conversion int to X possible
foo(X(2));                  // OK: direct initialization: explicit conversion
foo(static_cast<X>(2));     // OK: explicit conversion

bar(2);                     // OK: implicit conversion via Y(int) 
bar(Y(2));                  // OK: direct initialization
bar(static_cast<Y>(2));     // OK: explicit conversion

オブジェクトの初期化:

X x2 = 2;                   // error: no implicit conversion int to X possible
X x3(2);                    // OK: direct initialization
X x4 = X(2);                // OK: direct initialization
X x5 = static_cast<X>(2);   // OK: explicit conversion 

Y y2 = 2;                   // OK: implicit conversion via Y(int)
Y y3(2);                    // OK: direct initialization
Y y4 = Y(2);                // OK: direct initialization
Y y5 = static_cast<Y>(2);   // OK: explicit conversion

変換関数に関する例:

X x1{ 0 };
Y y1{ 0 };

関数の引数の変換:

baz(x1);                    // error: X not implicitly convertible to Z
baz(Z(x1));                 // OK: explicit initialization
baz(static_cast<Z>(x1));    // OK: explicit conversion

baz(y1);                    // OK: implicit conversion via Y::operator Z()
baz(Z(y1));                 // OK: direct initialization
baz(static_cast<Z>(y1));    // OK: explicit conversion

オブジェクトの初期化:

Z z1 = x1;                  // error: X not implicitly convertible to Z
Z z2(x1);                   // OK: explicit initialization
Z z3 = Z(x1);               // OK: explicit initialization
Z z4 = static_cast<Z>(x1);  // OK: explicit conversion

Z z1 = y1;                  // OK: implicit conversion via Y::operator Z()
Z z2(y1);                   // OK: direct initialization
Z z3 = Z(y1);               // OK: direct initialization
Z z4 = static_cast<Z>(y1);  // OK: explicit conversion

explicit変換関数またはコンストラクターを使用する理由

変換コンストラクターと非明示的な変換関数は、あいまいさをもたらす可能性があります。

Vに変換可能な 構造体 、から暗黙的に構築可能intな構造体、およびに対してそれぞれオーバーロードされた関数を考えてみましょう。UVfUbool

struct V {
  operator bool() const { return true; }
};

struct U { U(V) { } };

void f(U) { }
void f(bool) {  }

fタイプ のオブジェクトを渡す場合、への呼び出しはあいまいですV

V x;
f(x);  // error: call of overloaded 'f(V&)' is ambiguous

Uコンパイラは、 のコンストラクターまたは変換関数を使用して、Vオブジェクトを に渡すための型に変換するかどうかを知りませんf

のコンストラクターUまたは の変換関数のいずれかVexplicitである場合、非明示的な変換のみが考慮されるため、あいまいさはありません。両方が明示的であるf場合、型のオブジェクトを使用する呼び出しVは、明示的な変換またはキャスト操作を使用して行う必要があります。

変換コンストラクターと非明示的な変換関数は、予期しない動作を引き起こす可能性があります。

いくつかのベクトルを出力する関数を考えてみましょう:

void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }

ベクトルのサイズ コンストラクターが明示的でない場合は、次のように関数を呼び出すことができます。

print_intvector(3);

そのような電話から何を期待するでしょうか??を含む 1 行3または 3 行 0(2 つ目は何が起こるかです。)

クラス インターフェイスで明示的なキーワードを使用すると、インターフェイスのユーザーは、目的の変換について明示的になる必要があります。

Bjarne Stroustrup が (「The C++ Programming Language」、第 4 版、35.2.1、pp. 1011 で) なぜstd::duration単純な数値から暗黙的に構築できないのかという質問について次のように述べています。

言いたいことがわかっている場合は、それについて明確に説明してください。

于 2015-07-10T23:48:58.157 に答える
51

この回答は、他の回答ではカバーされていないため、明示的なコンストラクターを使用する/使用しないオブジェクトの作成に関するものです。

明示的なコンストラクターのない次のクラスを検討してください。

class Foo
{
public:
    Foo(int x) : m_x(x)
    {
    }

private:
    int m_x;
};

クラス Foo のオブジェクトは、次の 2 つの方法で作成できます。

Foo bar1(10);

Foo bar2 = 20;

実装によっては、クラス Foo をインスタンス化する 2 番目の方法がわかりにくい場合や、プログラマーの意図とは異なる場合があります。コンストラクターの前にキーワードを付けると、explicitでコンパイラ エラーが発生しFoo bar2 = 20;ます。

実装で明確に禁止されていない限り、単一引数のコンストラクターを として宣言することは、通常は良い習慣です。explicit

また、コンストラクターは

  • すべてのパラメータのデフォルト引数、または
  • 2 番目以降のパラメータのデフォルト引数

どちらも単一引数のコンストラクターとして使用できます。したがって、これらも作成することをお勧めしますexplicit

単一引数コンストラクターを明示的にしたくない場合の例は、ファンクターを作成している場合です (この回答で宣言されている 'add_x' 構造体を見てください)。そのような場合、オブジェクトを作成することadd_x add30 = 30;はおそらく理にかなっています。

ここには、明示的なコンストラクターに関する優れた記事があります。

于 2013-10-08T14:43:46.483 に答える
44

キーワードはexplicit、変換コンストラクターを非変換コンストラクターにします。その結果、コードはエラーが発生しにくくなります。

于 2012-11-21T02:36:55.113 に答える
35

-キーワードを使用して、コンストラクターを明示的explicitに呼び出すように強制できます。

class C {
public:
    explicit C() =default;
};

int main() {
    C c;
    return 0;
}

explicitコンストラクターの前にある- キーワードはC()、このコンストラクターへの明示的な呼び出しのみが許可されることをコンパイラーに伝えます。

-explicitキーワードは、ユーザー定義の型キャスト演算子でも使用できます。

class C{
public:
    explicit inline operator bool() const {
        return true;
    }
};

int main() {
    C c;
    bool b = static_cast<bool>(c);
    return 0;
}

ここで、explicit-keyword は明示的なキャストのみを有効にするよう強制するためbool b = c;、この場合は無効なキャストになります。このような状況では、explicit-keyword は、プログラマーが暗黙的で意図しないキャストを回避するのに役立ちます。この使用法はC++11で標準化されています。

于 2013-05-14T09:28:39.317 に答える