C++ には「変換コンストラクター」または「変換コンストラクター」と呼ばれるものがあると聞きました。これらは何ですか、何のためにあるのでしょうか? このコードに関して言及されているのを見ました:
class MyClass
{
public:
int a, b;
MyClass( int i ) {}
}
int main()
{
MyClass M = 1 ;
}
C++ には「変換コンストラクター」または「変換コンストラクター」と呼ばれるものがあると聞きました。これらは何ですか、何のためにあるのでしょうか? このコードに関して言及されているのを見ました:
class MyClass
{
public:
int a, b;
MyClass( int i ) {}
}
int main()
{
MyClass M = 1 ;
}
変換コンストラクターの定義は、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を使用します。さらに、 を実行してこの関数を呼び出すことができます。これも変換です。それらは変換であるため、それらが使用するコンストラクターがコンストラクターを変換することは理にかなっています。float
int
bar({2.5f, 10})
foo
したがって、 whichのコンストラクターに関数指定子を持たせるfloat
と、上記のコードのコンパイルが停止することに注意することが重要です。上記の新しい構文は、ジョブを実行するために利用可能な変換コンストラクターがある場合にのみ使用できます。int
explicit
C++11 : §6.6.3:
ブレース初期化リストを持つ
return
ステートメントは、指定された初期化子リストからのコピー リスト初期化 (8.5.4) によって、関数から返されるオブジェクトまたは参照を初期化します。
§8.5:
引数渡し[...]で発生する[...]初期化は、コピー初期化と呼ばれます。
§12.3.1:
明示的なコンストラクターは、非明示的なコンストラクターと同様にオブジェクトを構築しますが、直接初期化構文 (8.5) またはキャスト (5.2.9、5.4) が明示的に使用されている場合にのみ構築します。
コンストラクターを変換して暗黙的に変換する
質問の例をより複雑にしましょう
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
キーワードは、コンストラクターの暗黙的な変換を防ぐために常に使用され、クラス宣言のコンストラクターに適用されます。