web-dev-qa-db-ja.com

ユーザー定義の変換シーケンス

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{};
} 
19
User8500049

コンパイラは連続したユーザー定義の変換を実行しません

あなたの先生は正しいです。コードサンプルでは、​​次のように割り当てると、MyclassMyclass1に変換できないことを意味します。

Myclass2 m2 = Myclass{};

コンストラクターはMyclass1の作成時にMyclass2を予期し、コンパイラーはMyclassMyclass1に連続して変換してからMyclass2の作成に使用できないためです。ただし、次の行がある場合:

Myclass1 m2 = Myclass{};

Myclass1のコンストラクターはMyclassを引数として取るため、これは機能します。

更新:

これが機能する理由を尋ねることができます。

Myclass2 m2 {Myclass{}};

この場合、コンストラクターが呼び出され、Myclass1explicitとして宣言しない限り、変換を暗黙的に実行できます。これにより、コードのコンパイルが失敗します(ありがとう Fureeish リマインダー)。 :

Myclass2 m2 = Myclass{};

参照が必要なcopy-constructorを呼び出すようなものです。したがって、このように書くと、機能します。

Myclass2 m2 = Myclass1(Myclass{});

[〜#〜] evg [〜#〜] で述べたように、適合モード(/ permit-)がアクティブになっていない場合、Myclass2 m2 = Myclass{};はVS2017で受け入れられます。

13
Afshin

この線

 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として扱います。例については、 このドキュメント を参照してください。

8
Evg

他の答えは、リードを埋めることです:あなたが書いたコードは確かに無効です。 MSVCはデフォルトでそれを受け入れますが、MSVCはそうするのは間違っています。コマンドラインスイッチ/permissive-を使用して、MSVCを強制的に厳しくすることができます。 (そのスイッチを使用する必要があります

他のコンパイラ(GCC、clang)、拒否します。

他の回答に示されているように、コピーの初期化を直接の初期化に変更すると、すべてのコンパイラがコードを受け入れます。

2
Konrad Rudolph