59

C++ には「変換コンストラクター」または「変換コンストラクター」と呼ばれるものがあると聞きました。これらは何ですか、何のためにあるのでしょうか? このコードに関して言及されているのを見ました:

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
}

 int main()
{
    MyClass M = 1 ;
}
4

3 に答える 3

71

変換コンストラクターの定義は、C++03 と C++11 で異なります。どちらの場合もexplicitコンストラクターではない必要があります (そうでない場合は暗黙的な変換に関与しません) が、C++03 の場合は単一の引数で呼び出し可能でなければなりません。あれは:

struct foo
{
  foo(int x);              // 1
  foo(char* s, int x = 0); // 2
  foo(float f, int x);     // 3
  explicit foo(char x);    // 4
};

コンストラクター 1 と 2 は、どちらも C++03 と C++11 の変換コンストラクターです。2 つの引数を取る必要があるコンストラクター 3 は、C++11 では変換コンストラクターのみです。最後のコンストラクター 4 は、 であるため、変換コンストラクターではありませんexplicit

  • C++03 : §12.3.1

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

  • C++11 : §12.3.1

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

複数のパラメーターを持つコンストラクターが C++11 でコンストラクターを変換していると見なされるのはなぜですか? これは、新しい標準が、引数を渡したり、braced-init-listsを使用して値を返したりするための便利な構文を提供するためです。次の例を検討してください。

foo bar(foo f)
{
  return {1.0f, 5};
}

戻り値を波括弧初期化リストとして指定する機能は、変換と見なされます。これは、とfooを取る変換コンストラクタ forを使用します。さらに、 を実行してこの関数を呼び出すことができます。これも変換です。それらは変換であるため、それらが使用するコンストラクターがコンストラクターを変換することは理にかなっています。floatintbar({2.5f, 10})

fooしたがって、 whichのコンストラクターに関数指定子を持たせるfloatと、上記のコードのコンパイルが停止することに注意することが重要です。上記の新しい構文は、ジョブを実行するために利用可能な変換コンストラクターがある場合にのみ使用できます。intexplicit

  • C++11 : §6.6.3:

    ブレース初期化リストを持つreturnステートメントは、指定された初期化子リストからのコピー リスト初期化 (8.5.4) によって、関数から返されるオブジェクトまたは参照を初期化します。

    §8.5:

    引数渡し[...]で発生する[...]初期化は、コピー初期化と呼ばれます。

    §12.3.1:

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

于 2013-02-25T22:31:43.570 に答える
20

コンストラクターを変換して暗黙的に変換する

質問の例をより複雑にしましょう

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
     MyClass( const char* n, int k = 0 ) {}
     MyClass( MyClass& obj ) {}
}

最初の 2 つのコンストラクターは、コンストラクターを変換しています。3 つ目はコピー コンストラクターであり、別の変換コンストラクターです。

変換コンストラクターにより、引数の型からコンストラクターの型への暗黙的な変換が可能になります。intここで、最初のコンストラクターは からclass のオブジェクトへの変換を可能にしますMyClass。2 番目のコンストラクターは、文字列から class のオブジェクトへの変換を可能にしますMyClass。そして 3 番目に... クラスMyClassのオブジェクトからクラスのオブジェクトへMyClass!

変換コンストラクターであるためには、コンストラクターは単一の引数を持ち (2 番目の引数では、2 番目の引数は 1 つの既定値を持ちます)、 keyword なしで宣言する必要がありますexplicit

次に、メインの初期化は次のようになります。

int main()
{
    MyClass M = 1 ;
    // which is an alternative to
    MyClass M = MyClass(1) ;

    MyClass M = "super" ;
    // which is an alternative to
    MyClass M = MyClass("super", 0) ;
    // or
    MyClass M = MyClass("super") ;
}

明示的なキーワードとコンストラクター

explicitでは、キーワードを使用した場合はどうなるでしょうか。

class MyClass
{
  public:
     int a, b;
     explicit MyClass( int i ) {}
}

その後、コンパイラは受け入れません

   int main()
    {
        MyClass M = 1 ;
    }

これは暗黙の変換であるためです。代わりに、書く必要があります

   int main()
    {
        MyClass M(1) ;
        MyClass M = MyClass(1) ;
        MyClass* M = new MyClass(1) ;
        MyClass M = (MyClass)1;
        MyClass M = static_cast<MyClass>(1);
    }

explicitキーワードは、コンストラクターの暗黙的な変換を防ぐために常に使用され、クラス宣言のコンストラクターに適用されます。

于 2013-02-25T22:09:36.250 に答える