有趣的問題:
#include <iostream>
#include <array>
#include <tuple>
#include <typeinfo>
using std::cout;
using std::endl;
struct SomeClass
{
int baz;
SomeClass(int _b): baz(_b) {
cout << __PRETTY_FUNCTION__ << " = " << baz << endl;
}
SomeClass(SomeClass&&) {
cout << __PRETTY_FUNCTION__ << endl;
}
SomeClass(const SomeClass&) {
cout << __PRETTY_FUNCTION__ << endl;
}
};
template<typename T> void tell(T&& a)
{
cout << "Tell: " << __PRETTY_FUNCTION__ << " = " << a.baz << endl;
}
int main()
{
// one
cout << "= 1 =" << endl;
auto [one, two] = std::array<SomeClass,2>{SomeClass{1}, SomeClass{2}};
cout << "===" << endl;
tell(one); tell(two);
// two
cout << endl << "= 2 =" << endl;
auto [one2, two2] = std::make_tuple(SomeClass{1}, SomeClass{2});
cout << "===" << endl;
tell(one2); tell(two2);
// three
cout << endl << "= 3 =" << endl;
struct Something { SomeClass one{1}, two{2}; };
auto [one3, two3] = Something{};
cout << "===" << endl;
tell(one3); tell(two3);
return 0;
}
生成輸出:
= 1 =
SomeClass::SomeClass(int) = 1
SomeClass::SomeClass(int) = 2
===
Tell: void tell(T&&) [with T = SomeClass&] = 1
Tell: void tell(T&&) [with T = SomeClass&] = 2
= 2 =
SomeClass::SomeClass(int) = 2
SomeClass::SomeClass(int) = 1
SomeClass::SomeClass(SomeClass&&)
SomeClass::SomeClass(SomeClass&&)
===
Tell: void tell(T&&) [with T = SomeClass&] = 0
Tell: void tell(T&&) [with T = SomeClass&] = 4199261
= 3 =
SomeClass::SomeClass(int) = 1
SomeClass::SomeClass(int) = 2
===
Tell: void tell(T&&) [with T = SomeClass&] = 1
Tell: void tell(T&&) [with T = SomeClass&] = 2
第二種情況使用複製或移動(如果可用)構造函數。值沒有初始化,因爲我故意沒有在構造函數中這樣做。
有結合
在第二種情況下三種協議(對不起,我不無法訪問C++ 17 pdf,因此cppreference):
每個標識符都變成一個變量,其類型爲「引用 std::tuple_element<i, E>::type
」:如果其對應的 初始值設定項是左值,則以右值爲參考,否則爲左值引用。爲第i個的標識符的初始化 是
e.get<i>()
,如果查找使標識符於E的按類別成員訪問查找的範圍得到找到至少一個聲明
- 否則(的任何 那種),
get<i>(e)
,其中得到的是由參數相關的查找只擡頭,從而忽略非ADL查找
第一和示例的第二階段實際上是綁定元組類型。 但是...在第二階段我們用什麼來初始化?構造元組的模板函數:
std::make_tuple(SomeClass{1}, SomeClass{2});
它實際上會複製或移動值。可能會出現進一步的複製省略,但
auto t = std::make_tuple(SomeClass{1}, SomeClass{2});
auto [one2, two2] = t;
會產生這樣的輸出:雖然正常脫糖結構結合的模樣
SomeClass::SomeClass(int) = 2
SomeClass::SomeClass(int) = 1
SomeClass::SomeClass(SomeClass&&) //make_tuple
SomeClass::SomeClass(SomeClass&&)
SomeClass::SomeClass(const SomeClass&) //assignment
SomeClass::SomeClass(const SomeClass&)
:
auto t = std::make_tuple(SomeClass{1}, SomeClass{2});
auto& one2 = std::get<0>(t);
auto& two2 = std::get<1>(t);
和輸出匹配原:
SomeClass::SomeClass(int) = 2
SomeClass::SomeClass(int) = 1
SomeClass::SomeClass(SomeClass&&)
SomeClass::SomeClass(SomeClass&&)
===
因此,發生的複製或移動操作來自構建我們的tuple
。 我們要避免這種情況,如果我們採用通用的引用構造元組,那麼這兩個脫
auto t = std::tuple<SomeClass&&, SomeClass&&>(SomeClass{1}, SomeClass{2});
auto& one2 = std::get<0>(t);
auto& two2 = std::get<1>(t);
和結構結合
auto [one2, two2] = std::tuple<SomeClass&&, SomeClass&&>(SomeClass{1}, SomeClass{2});
會導致複製省略。
是的,很容易明白爲什麼當你考慮什麼結構綁定實際上desugar。 ; - ] – ildjarn
@ildjarn是你是否確認'one'和'two'不會導致copy elision但是'three'會? – Curious
我的意思是'是的,複製elision和結構化綁定一起工作' - 'one'和'three'將導致有保證的副本省略,'two'不會。 – ildjarn