2016-02-16 3 views
14

私はABという2つのクラスを持っていて、それぞれがBへの変換を定義しています。 ABに変換演算子を持ち、BAのコンストラクタを持ちます。 static_cast<B>への呼び出しはあいまいではありませんか? g ++を使用して、このコードは変換コンストラクタをコンパイルして選択します。このコードではあいまいな変換エラーが発生しませんか?

#include<iostream> 

using namespace std; 

struct B; 
struct A { 
    A(const int& n) : x(n) {} 
    operator B() const;   //this const doesn't change the output of this code 
    int x; 
}; 

struct B{ 
    B(const double& n) : x(n) {} 
    B(const A& a); 
    double x; 
}; 

A::operator B() const   //this const doesn't change the output of this code 
{ 
    cout << "called A's conversion operator" << endl; 
    return B(double(x)); 
} 

B::B(const A& a) 
{ 
    cout << "called B's conversion constructor" << endl; 
    x = (double) a.x; 
} 

int main() { 
    A a(10); 
    static_cast<B>(a);   // prints B's conversion constructor 
} 
+0

興味深い。私はその答えをテストするためにコードを更新しました。この答えは変換コンストラクタを呼び出すのに対して、変換演算子は呼び出されるべきです。 – roro

+2

あなたは正しいと思います。質問はかなり似ているので、冗談のように見えますが、似ていません。 –

答えて

13

ユーザ定義の変換シーケンスの場合。変換コンストラクタと変換演算子の間に優先順位がないように見えますが、どちらも候補です。

§13.3.3.1.2/ 1 ユーザ定義変換配列

ユーザ定義の変換シーケンスは続くユーザ定義変換(12.3)、続いて初期基準変換配列からなります第2の標準変換シーケンス。ユーザ定義の変換がコンストラクタ(12.3.1)によって指定されている場合、初期標準変換シーケンスはソースタイプをコンストラクタの引数で必要とされるタイプに変換します。ユーザ定義の変換が変換関数(12.3.2)によって指定されている場合、初期標準変換シーケンスはソース関数を変換関数の暗黙のオブジェクトパラメータに変換します。

したがって、変換が完了していれば、

B b2 = a; // ambiguous? 

それは曖昧かもしれないとコンパイルが失敗します。 Clangはコンパイルに失敗し、g ++はコードを受け入れてコンストラクタを使用します。 demo code、VSもコードを受け入れます。 VSとg ++は(OPコードごとに)変換コンストラクタを呼び出します。

投稿コードを考慮すると、コンバーターと変換演算子によるユーザー定義の変換シーケンスと、static_castの使用を考慮する必要があります。

§5.2.9/ 4 静的宣言T t(e);がよく形成されていれば式eが明示的にいくつかの発明のために、フォームstatic_cast<T>(e)static_castを用いるタイプTに変換することができる

キャスト一時変数t(8.5)。このような明示的な変換の効果は、宣言と初期化を実行してから変換の結果として一時変数を使用することと同じです。式eは、初期化でそれが左辺値として使用されている場合にのみglvalueとして使用されます。

上記の引用から、static_castB temp(a);に相当し、そのようなものとして、直接的な初期化シーケンスが使用されます。コンストラクタ

によって

§13.3.1.3/ 1 初期クラスタイプのオブジェクトは直接初期化(8.5)である場合、同一の発現または派生クラス型からコピー初期化(8.5 )、またはdefault-initialized(8.5)の場合、オーバーロードの解像度はコンストラクタを選択します。直接初期化またはデフォルト初期化の場合、候補関数はすべて、初期化されるオブジェクトのクラスのコンストラクタです。コピー初期化の場合、候補関数はすべてそのクラスの変換コンストラクタ(12.3.1)です。引数リストは、イニシャライザの式リストまたは代入式です。一般的に

explicitconst懸念としてマークされたコンストラクタと演算子を除く)best viable functionを考えるとき、それは完全な一致を提供していますから、AからB(const A& a);コンストラクタとBの建設与えられ、コンストラクタは勝つ必要があります; implicit conversionsは不要です(13.3項;オーバーロードの解決)。コンストラクタB(const A& a);が削除された場合は、ユーザー定義変換演算子が候補であり、その使用が曖昧ではないので


static_cast<>を有する変換が(まだ成功するであろう。

§13.3.1.4/ 1 8.5で指定された条件の下では、ユーザー定義の変換

によってクラスのコピー初期化、クラスタイプのオブジェクトのコピー初期化の一部として、ユーザ定義の変換ができ初期化式を初期化されるオブジェクトの型に変換するために呼び出されます。

引用符は、C++標準のN4567ドラフトから引用されています。


また、オブジェクトの構築の外で、つまりメソッドを呼び出すだけで、ユーザー定義の変換シーケンスを呼び出すことも有益です。

コードリスト(および上記の規則)が指定されている場合。

#include <iostream> 
using namespace std; 
struct A; 
struct B { 
    B() {} 
    B(const A&) { cout << "called B's conversion constructor" << endl; } 
}; 
struct A { 
    A() {} 
    operator B() const { cout << "called A's conversion operator" << endl; return B(); } 
}; 
void func(B) {} 
int main() { 
    A a; 
    B b1 = static_cast<B>(a); // 1. cast 
    B b2 = a; // 2. copy initialise 
    B b3 (a); // 3. direct initialise 
    func(a); // 4. user defined conversion 
} 

クラン、G ++(demo)及びVSは異なる結果と従ってコンプライアンスの可能性の異なるレベルを提供します。

  • 打ち鳴らすには、2と4
  • 3を介して
  • VSは、上記のルール4.

、1.失敗1. 4を介して受け付け++

  • グラムは、すべてのことから成功するはず失敗しますB変換コンストラクタは候補であり、それ以上のユーザ変換は必要ありません。これらのフォームには、直接構築とコピーの初期化が使用されます。標準(上記の抜粋、特に13.3.3.1.2/1と13.3.1.4/1、8.5/17.6.2)、2と4を読むことは失敗し、あいまいではない - コンバージョンコンストラクタとコンバージョン演算子は明確な順序付けがない状態で考慮されているからです。

    これは、意図しないユースケース(このようにして相互に変換できるタイプで、1つの変換シーケンスが存在する場所は一般的な使用例です)があると思います。

  • +0

    2と4は失敗すると思います。コピーの初期化*(ただし、同じタイプの式からではありません - over.match.copyで覆われているので、over.match.ctorではありません) 2つのユーザー定義シーケンスです。 –

    +0

    @MM可能ですが、[over.match.ctor]が明示的に "コピー初期化の場合、候補関数は次のようになります(2つのユーザー定義シーケンスは常に曖昧です。そのクラスのすべての変換コンストラクタ(12.3.1) "両方の解釈が適用できると思います。 – Niall

    +0

    [over.match.ctor]は、 "同じまたは派生クラスタイプの式からコピー初期化されました"と言います。 (段落の残りの部分は、最初の文で選択されたケースにのみ適用されます) –

    関連する問題