2016-05-13 18 views
10

私は、転送コンストラクタを使って薄い派生クラスを作っていました。 (私と一緒にいて、継承されたコンストラクタがないGCC 4.7.2を使う必要があります)。この演算子はなぜ曖昧な呼び出しですか?

最初の試行では、explicitというキーワードを追加するのを忘れてしまい、エラーが発生しました。誰かがこの特定のエラーがなぜ発生するのか正確に説明できますか?私はイベントのシーケンスを理解するのに問題があります。

#include <memory> 

template<typename T> 
struct shared_ptr : std::shared_ptr<T> 
{ 
    template<typename...Args> 
    /*explicit*/ shared_ptr(Args &&... args) 
    : std::shared_ptr<T>(std::forward<Args>(args)...) 
    {} 
}; 

struct A {}; 

struct ConvertsToPtr 
{ 
    shared_ptr<A> ptr = shared_ptr<A>(new A()); 
    operator shared_ptr<A> const &() const { return ptr; } 
}; 

int main() 
{ 
    shared_ptr<A> ptr; 
    ptr = ConvertsToPtr(); // error here 
    return 0; 
} 

エラー:

test.cpp: In function ‘int main()’: 
test.cpp:28:23: error: ambiguous overload for ‘operator=’ in ‘ptr = ConvertsToPtr()’ 
test.cpp:28:23: note: candidates are: 
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(const shared_ptr<A>&) 
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(shared_ptr<A>&&) 

答えて

7

これは、次のとg++ 4.8.4の場合である:
g++ -g -pedantic --std=c++11 -o test main.cpp
VS2015の設定はすべてデフォルトに設定されています。

問題は、コンパイラがConvertsToPtr()によって返された一時的なものをshared_ptrオブジェクトに変換しようとしていることです。コンパイラがexplicitキーワードで使用されると、この変換はコンストラクタを使用して実行されることはありません。しかし、gdbで調べると、shared_ptr<A> const &()変換関数を使用して、適切なタイプに一致しているように見えます。この変換は、const shared_ptr &を返します。これは代入演算子を呼び出すときにあいまい性がありません(これはwojciech Frohmbergの結果とも一致します)。

ただし、explicitを省略すると、shared_ptrのオブジェクトが返されます。これは、代入演算子のrvalueバージョンまたはconst lvalueバージョンのいずれかに一致させることができます。 N4296によると

、表-11、そして我々は、conversion constructorrvalue of shared_ptr対象と建設後。ただし、オーバーロードの解像度は2つの一致を検出し、両方ともExact Match(ランクバージョンはIdentity matching、もう1つはQualification matching)です。

VS2015でも確認しましたが、コメントに記載されているように動作します。しかし、coutのデバッグを使用すると、定数値の割り当ての値が、の値 const lvalue refrenceのバージョンに優先して優先されます。

EDIT:標準で少し深く見えて、修正を追加しました。結果に関する削除されたテキストVS2015は両方の割り当てを定義していなかったので間違っていました。両方の割り当てが宣言されたとき、それはrvalueを好む。

私はVSコンパイラがからの区別をQualificationと区別しているとします。しかし私が結論づけているように、VSコンパイラはバグです。 g++コンパイラは与えられた標準に従います。しかし、GCC 5.0はVisual Studioとして動作しているので、コンパイラのバグの可能性は低いですし、別のエキスパートの洞察を見てうれしいです。

EDIT:13.3.3。2より良いランキング私はそれについて書いた後のドローブレーカーの一つは、次のとおりです。

— S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.

与えられた右辺値(ない右辺値参照)がconst int &&const int &上に一致するようになっていることを示す添付の例があります。したがって、たとえ&&タイプであってもconst &&タイプではなく、私たちのケースに関連していると仮定することは安全です。結局のところ、GCC 4.7.4.8はバグです。

+0

確かにrvaluesはlvalue定数参照にバインドされます。 'void f(float const&)'を '0f'で呼び出してみてください。 – AndyJost

+1

したがって、 'ConvertsToPtr'を構築した後、コンパイラは' shared_ptr :: operator = 'を検索して、オーバーロード解決を続けます。 'shared_ptr const&'を受け入れる候補は、変換演算子のために成功します。 'explicit'がなければ、' shared_ptr && 'を受け入れる候補もコンストラクタベースの変換によって成功します。いずれの変換も優れていないため、呼び出しはあいまいです。意味をなさない – AndyJost

+0

@AndyJost:C++ではシーケンス内で2つのユーザー定義変換が許可されていないため、最短の変換ルートが明らかに優れているため、これはコンパイラのバグです。 –

関連する問題