74

変換演算子とコンストラクターに関するSOに関するいくつかの質問をここで読んだことで、それらの間の相互作用、つまり「あいまいな」呼び出しがある場合について考えるようになりました。次のコードを検討してください。

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なるべきかについて、誰がもっと知っていますか?C ++によると、答えは次のとおりです。オブジェクト指向の実践で、これが当てはまるはずだと示唆するものはありますか?個人的にはどちらにしても意味があるので、どうやって選んだのか知りたいです。BABA

前もって感謝します

4

2 に答える 2

56

コピーの初期化を行います。変換シーケンスで変換を行うと見なされる候補関数は、変換関数と変換コンストラクターです。これらはあなたの場合です

B(const A&)
operator B() 

さて、それはあなたがそれらを宣言する方法です。過負荷解決はそれを抽象化し、各候補を呼び出しの引数に対応するパラメーターのリストに変換します。パラメータは次のとおりです

B(const A&)
B(A&)

2つ目は、変換関数がメンバー関数であるためです。これA&は、候補がメンバー関数である場合に生成される、いわゆる暗黙のオブジェクトパラメータです。現在、引数の型はAです。暗黙的なオブジェクトパラメータをバインドする場合、非定数参照は右辺値にバインドできます。したがって、別のルールでは、パラメーターが参照である2つの実行可能な関数がある場合、const資格が最も少ない候補が勝ちます。そのため、変換関数が優先されます。operator Bconstメンバー関数を作成してみてください。あいまいさに気付くでしょう。

オブジェクト指向の哲学的観点から、これはコードの動作方法ですか?AオブジェクトがBオブジェクト、AまたはBになる方法について誰がもっと知っていますか?C ++によると、答えはAです。オブジェクト指向の実践でこれが当てはまるはずだと示唆するものはありますか?個人的にはどちらにしても意味があるので、どうやって選んだのか知りたいです。

ちなみに、変換関数をconstメンバー関数にすると、GCCはコンストラクターを選択します(GCCは、コンストラクターとBの取引が多いと考えているようです)。-pedantic診断を行うには、ペダンティックモード( )に切り替えます。


標準、8.5/14

それ以外の場合(つまり、残りのコピー初期化の場合)、ソースタイプからデスティネーションタイプに、または(変換関数が使用される場合は)その派生クラスに変換できるユーザー定義の変換シーケンスが、13.3で説明されているように列挙されます。 1.4であり、過負荷解決(13.3)によって最適なものが選択されます。

13.3.1.4

過負荷解決は、呼び出されるユーザー定義の変換を選択するために使用されます。「cv1T」が初期化されるオブジェクトのタイプであり、Tがクラスタイプであると仮定すると、候補関数は次のように選択されます。

  • Tの変換コンストラクター(12.3.1)は候補関数です。
  • イニシャライザ式の型がクラス型「cvS」の場合、Sとその基本クラスの変換関数が考慮されます。S内に隠されておらず、cv非修飾バージョンがTと同じ型であるか、その派生クラスである型を生成するものは、候補関数です。「Xへの参照」を返す変換関数は、タイプXの左辺値を返すため、候補関数を選択するこのプロセスでXを生成すると見なされます。

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

13.3.3.2/3

  • [...] S1とS2が参照バインディング(8.5.3)であり、参照が参照するタイプがトップレベルのcvを除いて同じタイプである場合、標準変換シーケンスS1は標準変換シーケンスS2よりも優れた変換シーケンスです。 -修飾子、およびS2によって初期化された参照が参照するタイプは、S1によって初期化された参照が参照するタイプよりもcv修飾されています。
于 2009-09-05T19:05:22.580 に答える
3

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

MSVS は変換演算子の前に適切なコンストラクターを検索するだけだと思っていましたが、B のコンストラクターから const ワードを削除すると、A の演算子 B() の呼び出しが開始されることがわかりました。次のコードはまだ B のコンストラクターを呼び出しているため、おそらく一時オブジェクトに対していくつかの特別な動作があります。

A a;

B b = a;
于 2009-09-05T19:27:57.620 に答える