explicit
キーワードを勉強する前に、私の先生は「コンパイラーはユーザー定義の連続変換を実行しません」と言いました。それが本当の場合、私のコードにエラーはありますか?それとも私は私の先生を誤解しましたか?私はVS2017で働いています。
#include<iostream>
#include <string>
class Myclass {
public:
Myclass() {
std::cout << "Myclass" << std::endl;
}
};
class Myclass1 {
public:
Myclass1(Myclass m) {
std::cout << "Myclass1" << std::endl;
}
};
class Myclass2{
public:
Myclass2(Myclass1 m) {
std::cout << "Myclass2" << std::endl;
}
};
int main() {
Myclass2 m2 = Myclass{};
}
コンパイラは連続したユーザー定義の変換を実行しません
あなたの先生は正しいです。コードサンプルでは、次のように割り当てると、Myclass
をMyclass1
に変換できないことを意味します。
Myclass2 m2 = Myclass{};
コンストラクターはMyclass1
の作成時にMyclass2
を予期し、コンパイラーはMyclass
をMyclass1
に連続して変換してからMyclass2
の作成に使用できないためです。ただし、次の行がある場合:
Myclass1 m2 = Myclass{};
Myclass1
のコンストラクターはMyclass
を引数として取るため、これは機能します。
更新:
これが機能する理由を尋ねることができます。
Myclass2 m2 {Myclass{}};
この場合、コンストラクターが呼び出され、Myclass1
をexplicit
として宣言しない限り、変換を暗黙的に実行できます。これにより、コードのコンパイルが失敗します(ありがとう Fureeish リマインダー)。 :
Myclass2 m2 = Myclass{};
参照が必要なcopy-constructorを呼び出すようなものです。したがって、このように書くと、機能します。
Myclass2 m2 = Myclass1(Myclass{});
[〜#〜] evg [〜#〜] で述べたように、適合モード(/ permit-)がアクティブになっていない場合、Myclass2 m2 = Myclass{};
はVS2017で受け入れられます。
この線
Myclass2 m2 = Myclass{};
意味copy-initialization。引用 cppreference.com :
T
がクラス型であり、other
の型のcv非修飾バージョンがT
でないか、T
[...]から派生した場合、other
のタイプからT
[...]に変換できるユーザー定義の変換シーケンスが調べられ、過負荷の解決によって最適なものが選択されます。
引用 さらに :
ユーザー定義の変換は、0個または1個の非明示的な単一引数コンストラクター、または非明示的な変換関数呼び出しで構成されます。
したがって、Myclass2 m2 = Myclass{};
は、2つのユーザー定義の変換を伴うため、受け入れられません。
それでは、見てみましょう
Myclass2 m2 {Myclass{}};
Afshinの回答で提案されています。これは直接初期化です。ルールは 異なる :
T
のコンストラクターが調べられ、オーバーロード解決によって最適なものが選択されます。次に、コンストラクターが呼び出されてオブジェクトが初期化されます。
Myclass2
のコンストラクターはMyclass1
を受け入れ、Myclass
からMyclass1
を取得するには1つのユーザー定義の変換が必要です。したがって、コンパイルします。
VSではcopy-initilizationは、適合モード(/premissive-
)がアクティブ化されていない場合(デフォルト)、direct-initilizationのように扱われることに注意してください。したがって、VSはMyclass2 m2 = Myclass{};
を受け入れてdirect-initilizationとして扱います。例については、 このドキュメント を参照してください。
他の答えは、リードを埋めることです:あなたが書いたコードは確かに無効です。 MSVCはデフォルトでそれを受け入れますが、MSVCはそうするのは間違っています。コマンドラインスイッチ/permissive-
を使用して、MSVCを強制的に厳しくすることができます。 (そのスイッチを使用する必要があります)
他の回答に示されているように、コピーの初期化を直接の初期化に変更すると、すべてのコンパイラがコードを受け入れます。