强制复制省略是否适用于通过结构化绑定进行分解?以下哪种情况适用于?
// one
auto [one, two] = std::array<SomeClass>{SomeClass{1}, SomeClass{2}};
// two
auto [one, two] = std::make_tuple(SomeClass{1}, SomeClass{2});
// three
struct Something { SomeClass one, two; };
auto [one, two] = Something{};
我怀疑只有第三种情况允许复制省略,因为前两个将通过std::get<>
“分解”而std::tuple_size<>
和std::get<>
在参数为rvalues时返回xvalues
标准的引用也很好!
答案 0 :(得分:14)
强制复制省略是否适用于通过结构化绑定进行分解?以下哪种情况适用于?
是的,所有这些。结构化绑定的目的是为您提供对要绑定的类型的析构元素的命名引用。这样:
auto [one, two] = expr;
只是语法糖:
auto __tmp = expr;
some_type<0,E>& a = some_getter<0>(__tmp);
some_type<1,E>& b = some_getter<1>(__tmp);
some_type
和some_getter
取决于我们正在解构的类型类型(数组,类似元组,或者包含所有公共非静态数据成员的类型)。
强制性副本省略适用于auto __tmp = expr
行,其他行均不涉及副本。
评论中的示例存在一些混淆,所以让我详细说明会发生什么:
auto [one, two] = std::make_tuple(Something{}, Something{});
auto __tmp = std::make_tuple(Something{}, Something{}); // note that it is from
// std::make_tuple() itself that we get the two default constructor calls as well
// as the two copies.
using __E = std::remove_reference_t<decltype(__tmp)>; // std::tuple<Something, Something>
然后,由于__E
是not an array type而is tuple-like,我们通过unqualified call to get
looked up in the associated namespace of __E
引入变量。初始值设定项为xvalue,类型为rvalue references:
std::tuple_element_t<0, __E>&& one = get<0>(std::move(__tmp));
std::tuple_element_t<1, __E>&& two = get<1>(std::move(__tmp));
请注意,虽然one
和two
都是__tmp
的右值引用,decltype(one)
和decltype(two)
将both yield Something
而不是{{1} }}
答案 1 :(得分:2)
有趣的问题:
#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):
每个标识符都成为一个变量,其类型为&#34;引用
std::tuple_element<i, E>::type
&#34;:左值引用,如果它对应的话 初始化器是一个左值,右值参考。初始化程序 对于第i个标识符是
e.get<i>()
,如果通过类成员访问查找查找标识符在E的范围内找到至少一个声明(无论如何) 亲切)- 否则为
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});
会导致复制省略。