std::forward_as_tuple(x, y) 给你一个tuple<int&, double&>。绑定到其中的类型是int& 和double&(与绑定到tuple<int, double> 的类型相同的是int 和double)。基本上:
auto [a, b] = std::forward_as_tuple(x, y);
auto&& [c, d] = std::forward_as_tuple(x, y);
表现得好像:
auto __e = std::forward_as_tuple(x, y);
using __E = remove_reference_t<decltype(__e)>;
tuple_element_t<0, __E>&& a = std::get<0>(std::move(__e));
tuple_element_t<1, __E>&& b = std::get<1>(std::move(__e));
auto&& __f = std::forward_as_tuple(x, y);
using __F = remove_reference_t<decltype(__f)>;
tuple_element_t<0, F>&& c = std::get<0>(std::move(__f));
tuple_element_t<1, F>&& d = std::get<1>(std::move(__f));
所以a 是对int& 的右值引用,c 是对double& 的右值引用,所以分别是int& 和double&。这个特殊的公式(我特别称它为引用的引用,而不是只称它为int&)是必要的,因为decltype(name) 其中name 是一个结构化绑定为您提供了referenced 类型,这就是为什么decltype(a) 会给你int&。
上面还显示了[a, b] 和[c, d] 案例之间的区别:auto 与 auto&& 声明适用于我们正在解构的未命名对象。它不会影响绑定本身†。
这个案例:
auto&& [e, f] = std::tuple{x, y};
不提供参考,因为它解压到:
auto&& __g = std::tuple{x, y};
using __G = remove_reference_t<decltype(__g)>;
tuple_element_t<0, G>&& e = std::get<0>(std::move(__g));
tuple_element_t<1, G>&& f = std::get<1>(std::move(__g));
所以e 是对int 的右值引用,这意味着decltype(e) 是int,而不是int&。
如果有一些函数返回引用元组,我如何使用结构化绑定进行复制?
您无法使用结构化绑定制作副本。结构化绑定仅是关于解构一个对象,它根本不是关于改变任何东西。如果要复制,则必须手动进行:
std::tuple<int&, double&> f = /* ... */;
std::tuple<int, double> actual_copy = f;
auto& [x, y] = actual_copy;
†在上述情况下,因为被解构的底层对象是左值引用 (auto&),这在技术上使绑定本身成为左值引用而不是右值引用——虽然我不确定这是否真的是一个有意义的区别。