54

変換演算子とコンストラクタについてのここでのいくつかの質問を読んで、私はそれらの間の相互作用、つまり「あいまいな」呼び出しがあると考えました。次のコードを考えてみましょう:変換コンストラクタと変換演算子:precedence

class A; 

class B { 
     public: 
     B(){} 

     B(const A&) //conversion constructor 
     { 
       cout << "called B's conversion constructor" << endl; 
     } 
}; 

class A { 
     public: 
     operator B() //conversion operator 
     { 
       cout << "called A's conversion operator" << endl; 
       return B(); 
     } 
}; 

int main() 
{ 
    B b = A(); //what should be called here? apparently, A::operator B() 
    return 0; 
} 

上記のコードが表示され、コンストラクタとは対照的に、変換演算子が呼び出されることを意味し、「Aの変換演算子と呼ばれます」。 operator B()コードをAから削除/コメントアウトすると、コンパイラは代わりにコンストラクタを使用して(コードを変更しなくても)うれしいことになります。

私の質問は以下のとおりです。

  1. コンパイラはB b = A();があいまいな呼び出しであることを考慮していないので、ここでの仕事での優先順位のいくつかの種類が存在しなければなりません。この優先順位は正確にどこに設定されていますか? (C++標準の参照/引用は高く評価されます)
  2. オブジェクト指向の哲学的見地からは、これがコードの振る舞いと同じですか? AオブジェクトがBオブジェクト(AまたはB)になる方法についてもっと知っているのは誰ですか? C++によれば、答えはAです。オブジェクト指向の練習には、これがそうでなければならないことが示唆されています。個人的にはどちらにしても意味がありますので、どのように選択したのか知りたいです。

あなたは初期設定をコピーしてやる、と変換シーケンスで変換を行うと考えられる候補関数は変換関数と変換するコンストラクタです事前

+0

"//コピーコンストラクタ"はコピーコンストラクタではなく、コンストラクタです。 –

+0

あなたが正しいです、私はその言葉を誤解しました。私はそれを編集しました。 – GRB

答えて

42

に感謝します。これらはあなたの場合です

B(const A&) 
operator B() 

これはあなたがそれらを宣言する方法です。オーバーロードの解決はそれから抽象化し、各候補を呼び出しの引数に対応するパラメータのリストに変換します。パラメータは、

B(const A&) 
B(A&) 

です。第2の理由は、変換関数がメンバ関数であるためです。 A&は、候補がメンバ関数であるときに生成されるいわゆる暗黙的オブジェクトパラメータです。引数の型はAです。暗黙的なオブジェクトパラメータをバインドするとき、非const参照の値にバインドできます。つまり、パラメータが参照である2つの実行可能な関数を持つ場合、のうち最も小さいものが const修飾子を持つ候補が勝ちます。それがあなたのコンバージョン機能が勝つ理由です。 operator Bをconstメンバー関数にしてみてください。あいまいさに気づくでしょう。

オブジェクト指向の哲学的観点からは、これがコードの動作方法ですか? AオブジェクトがBオブジェクト、AまたはBになる方法についてもっと知っているのは誰ですか? C++によると、答えはAです - オブジェクト指向の練習には、これがそうでなければならないことを示唆する何かがありますか?個人的にはどちらにしても意味がありますので、どのように選択したのか知りたいです。レコードの

あなたは変換関数にconstメンバ関数を作る場合は、その後、GCCは、コンストラクタを選んだのだろう(そうGCCはBはそれでより多くのビジネスを持っていることを考えているようです?)。ペディティックモード(-pedantic)に切り替えると診断が行われます。


Standardese、そうでない場合8.5/14

(すなわち、残りのコピー初期ケース用)、宛先タイプにソース・タイプから変換したりすることができるユーザ定義の変換シーケンス(変換関数が使用される)からその派生クラスまでは13.3.1.4で列挙され、最良のものは過負荷解決(13.3)によって選択される。

そして13.3.1.4

オーバーロード解決が呼び出されるユーザ定義の変換を選択するために使用されます。次のように「CV1 Tが」Tクラスタイプで、初期化されるオブジェクトのタイプであると仮定すると、候補となる機能が選択される:

  • Tの変換コンストラクタ(12.3.1)は、候補関数です。
  • 初期化子式の型がクラス型 "cv S"である場合、Sとその基本クラスの変換関数が考慮されます。 S内に隠されず、cv非定格バージョンがTと同じ型またはその派生クラスである型を生成するものは、候補関数である。 "Xへの参照"を返す変換関数は、X型の左辺値を返すので、候補関数を選択するこのプロセスではXが返されます。

どちらの場合でも、引数リストには1つの引数があり、これは初期化式です。 [注:この引数は、コンストラクタの最初のパラメータと、変換関数の暗黙のオブジェクトパラメータと比較されます。 ]

そして13.3.3.2/3

  • 標準変換シーケンスS1 [...] S1、S2は参照バインディング(8.5.3)である場合、標準的な変換シーケンスS2よりも良好な変換シーケンスであり、そして参照が参照する型は最上位のcv修飾子を除いて同じ型であり、S2によって初期化された参照が参照する型は、S1によって初期化された参照型が参照する型よりもcv修飾されています。
+1

ああ、私の問題はコンストラクタと演算子の間の優先順位ではなく、それぞれのconstの性質です。あなたは正しく、 '演算子B()'を '演算子B()const'に変更するとあいまいなエラーになりました。 – GRB

+1

先ほどの例と同じように私は同じ「分析」を私の別の回答に入れています。http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy-初期化と代入initialize/1051468#1051468 xD –

+0

答えの最初の文についての簡単な質問 - 'あなたはコピーの初期化を行います... '。私はコピーがどこにも発生しているとは見ません。つまり、別のAから構築されたAはありません(別のBのBもありません)。初期化 'の代わりに'割り当て初期化 'の行に沿って何か?ありがとう! –

3

MSVS2008は、コンストラクタの選択について独自の見解を持っているようだ:それは関係なく、Aのオペレータのconst性のBにコピーコンストラクタを呼び出します。したがって、標準が正しい動作を指定している場合でも、ここでは注意してください。

MSVSは変換演算子の前に適切なコンストラクタを検索するだけで、Bのコンストラクタからconst単語を削除するとAの演算子B()を呼び出すことがわかりました。次のコードでもBのコンストラクタが呼び出されるため、おそらく一時オブジェクトの特殊な動作が発生します。

A a; 

B b = a; 
関連する問題