2016-11-16 20 views

答えて

2

{}は、代入の右側にあるoperator=の非明示的なコンストラクタを呼び出すことができます。

使用可能operator=過負荷である:

tuple& operator=(const tuple& other); 
tuple& operator=(tuple&& other); 
template< class... UTypes > 
tuple& operator=(const tuple<UTypes...>& other); 
template< class... UTypes > 
tuple& operator=(tuple<UTypes...>&& other); 
template< class U1, class U2 > 
tuple& operator=(const pair<U1,U2>& p); 
template< class U1, class U2 > 
tuple& operator=(pair<U1,U2>&& p); 

templateオペレータのオーバーロードは、{}(注:これはC++ 17に変更してもよい)からその種類を推測することができないまま、:

tuple& operator=(const tuple& other); 
tuple& operator=(tuple&& other); 

この場合、tuplestd::tuple<int&, int&>です。

tuple<Ts...>のタプルコンストラクタは、完全な要素順構築がexplicit(そのリストの#3)です。 {}は明示的なコンストラクタを呼び出さない。

条件付き非明示的コンストラクタは、Ts const&...をとります。 Tsがコピー不可能であり、int&がコピー不可能な場合は存在しません。

したがって、{int&, int&}から構築する実行可能なタイプはなく、オーバーロードの解決に失敗します。


標準ではこれが解決されないのはなぜですか?さて、私たちは自分でそれをすることができます!

この問題を解決するには、タイプがすべて参照の場合にのみ、tupleに特別な(Ts...)非明示的なコンストラクタを追加する必要があります。

私たちはおもちゃのタプル書く場合:

struct toy { 
    std::tuple<int&, int&> data; 
    toy(int& a, int& b):data(a,b) {} // note, non-explicit! 
}; 
toy toy_tie(int& a, int& b) { return {a,b}; } 

と、それを使用し、あなたはその

std::tie(a, b) = {b, a}; 

コンパイルと実行に気付くでしょう。

a%bint&にバインドすることはできませんしかし、

std::tie(a, b) = { b, a % b }; 

は、しません。

我々は、次いで、toyを増強することができる:(+特別なメンバ関数をデフォルトtemplate<class...>は、それが必要として、それは特別なメンバ関数よりも低い優先度を有している保証する。)

template<class...> 
toy& operator=(std::tuple<int, int> o) { 
    data = o; 
    return *this; 
} 

これにより、割り当て元は{int,int}になります。私たちはそれを実行し、間違った結果を得る。 5,20のgcdは20です。何が悪かったのか?参照にバインドされabの両方で

toy_tie(a, b) = std::tie(b, a); 

安全なコードではありません、それは

toy_tie(a, b) = { b, a }; 

が何をするかです。

要するに、この権利を行うことは難しいです。この場合、安全のために割り当てようとする前に、のコピーを右側に置く必要があります。コピーを取る時期とそうでないことを知っていることもやりにくいです。

この作業を暗黙的に行うと、エラーが発生しやすくなります。ある意味では、それがうまくいかず、(可能な限り)それを修正することは悪い考えのように見えます。

live example

+0

素晴らしい説明..私はPythonの複数の割り当て(例えばa、b = b、a%b)の影響を受けましたが、これは安全に(Pythonのように)毎回コピーを取る必要があります。 –

4

std::initializer_listは、アイテムの均質コレクションです。 std::initializer_liststd::tuple::operator=を定義することが妥当な唯一のケースは、タプルが均質で、初期化リストと同じサイズを持つ場合です(まれなことです)。

(Additional information in this question.)


策/回避策:あなたの代わりにstd::make_tupleを使用することができます。

int greatestCommonDivisor(int a, int b) 
{ 
    if (b > a) 
     std::tie(a, b) = std::make_tuple(b, a); 
    while (b > 0) 
     std::tie(a, b) = std::make_tuple(b, a % b); 
    return a; 
} 

...またはC++ 17 Template argument deduction for class templatesのおかげで)でstd::tupleのコンストラクタを

int greatestCommonDivisor(int a, int b) 
{ 
    if (b > a) 
     std::tie(a, b) = std::tuple{b, a}; 
    while (b > 0) 
     std::tie(a, b) = std::tuple{b, a % b}; 
    return a; 
} 

std::tie(a,b) = {b, a}; 

コンパイルされないのはなぜ

+1

'= {blah}'は必ずしも初期化子リストを参照するとは限りません。それは建設することができます。 – Yakk

関連する問題