我想知道爲什麼做出這個選擇。它將使寫了許多功能在一個非常明確和簡潔的方式..如:爲什麼不能爲std :: tuple分配一個初始化列表?
int greatestCommonDivisor(int a, int b)
{
if (b > a)
std::tie(a, b) = { b, a };
while (b > 0)
std::tie(a, b) = { b, a % b };
return a;
}
我想知道爲什麼做出這個選擇。它將使寫了許多功能在一個非常明確和簡潔的方式..如:爲什麼不能爲std :: tuple分配一個初始化列表?
int greatestCommonDivisor(int a, int b)
{
if (b > a)
std::tie(a, b) = { b, a };
while (b > 0)
std::tie(a, b) = { b, a % b };
return a;
}
爲什麼
std::tie(a,b) = {b, a};
無法編譯?
{}
在賦值的右側只能調用一個參數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);
其中在這種情況下,tuple
是std::tuple<int&, int&>
。
tuple<Ts...>
的元組構造函數完美轉發元素構造explicit(該列表中的#3)。 {}
不會調用顯式的構造函數。
有條件的非顯式構造函數需要Ts const&...
;它不存在如果Ts
是不可複製的,並且int&
是不可複製的。
所以沒有可行的類型來構建從{int&, int&}
,重載解決失敗。
爲什麼標準沒有解決這個問題?那麼,我們可以自己做!
爲了解決這個問題,我們必須添加一個特殊的(Ts...)
非顯式構造函數到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};
編譯和運行。
然而,
std::tie(a, b) = { b, a % b };
並不像a%b
不能綁定到int&
。
然後,我們可以增加toy
用:(+默認特殊成員函數template<class...>
確保它具有比特殊成員函數較低的優先級,因爲它應該)
template<class...>
toy& operator=(std::tuple<int, int> o) {
data = o;
return *this;
}
。
這讓我們從{int,int}
分配。然後我們運行它並得到錯誤的結果。 5,20
的gcd是20
。什麼地方出了錯?
toy_tie(a, b) = std::tie(b, a);
既a
和b
勢必引用是不是安全的代碼,而這正是
toy_tie(a, b) = { b, a };
一樣。
總之,這樣做的權利是棘手的。在這種情況下,您需要在分配安全之前採取右側的副本。知道什麼時候採取複製,何時不採取也很棘手。
讓這項工作隱含地看起來容易出錯。所以從某種意義上說,這是無效的,但修復它(儘管可能)看起來是一個壞主意。
std::initializer_list
是均勻收集的物品,而std::tuple
是異構。定義std::tuple::operator=
爲std::initializer_list
唯一有意義的情況是元組是均勻的並且具有與初始化程序列表相同的大小,這種情況很少見。
(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;
}
...或std::tuple
的構造函數在C++ 17 (感謝Template argument deduction for class templates):
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;
}
'= {blah}'並不總是指初始值列表;它可以是建築。 – Yakk
很好的解釋..我受到了python的多重任務(例如a,b = b,a%b)的影響,但爲了安全起見,您需要每次都複製一份文件(就像Python一樣) –