有趣的问题:
#include <iostream>
#include <array>
#include <tuple>
#include <typeinfo>
using std::cout;
using std::endl;
struct SomeClass
SomeClass(SomeClass&&)
SomeClass(const SomeClass&)
};
template<typename T> void tell(T&& a)
int main()
, SomeClass};
cout << "===" << endl;
tell(one); tell(two);
cout << endl << "= 2 =" << endl;
auto [one2, two2] = std::make_tuple(SomeClass, SomeClass);
cout << "===" << endl;
tell(one2); tell(two2);
cout << endl << "= 3 =" << endl;
struct Something , two; };
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可以找到至少一个声明(无论是什么类型)
- 否则,使用
get<i>(e)
,其中只通过参数相关的查找来查找get,忽略非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})
会导致复制省略。
one
和three
将会保证进行拷贝省略,但two
不会。 - ildjarnget<>
和tuple_size
函数/方法/特性来访问?按照标准的阅读,右侧的元素必须是数组或者有所有公共成员的类。能否以这种方式递归使用结构化绑定呢? - Curiousget<>
和tuple_size
都是无关紧要的。 - ildjarn