为什么结构化绑定依赖于 tuple_element?

Why do structured bindings depend on tuple_element?

most recent draft of the structured bindings proposal (on which the C++17 feature was based) requires std::tuple_size, member get or std::get, and std::tuple_element. Previous drafts 只需要 std::tuple_size 和成员 getstd::get。据我所知,没有讨论添加这个,它只是出现在最终草案中。是否有令人信服的理由要求 tuple_element 专业化,考虑到我相信它通常可以实现为

template<std::size_t index, typename T>
struct tuple_element {
    using type = decltype(std::get<index>(std::declval<T>()));
};

有谁知道为什么要添加此要求?

考虑案例:

std::tuple<int, int&>& foo();
auto& [x, y] = foo();

什么是decltype(x),什么是decltype(y)?语言功能的目标是 x 只是 foo().__0 的另一个名称,而 yfoo().__1 的另一个名称,这意味着它们应该是 intint&。按照今天的具体说明,这将解压缩为 :

auto& __e = foo();
std::tuple_element_t<0, decltype(__e)>& x = std::get<0>(__e);
std::tuple_element_t<1, decltype(__e)>& y = std::get<1>(__e);

并且规则起作用,使得 decltype(x)x 引用 的类型,因此 int。而decltype(y)y引用的类型,所以int&

如果我们避免 tuple_element,方法是:

auto&& x = std::get<0>(__e);
auto&& y = std::get<1>(__e);

那么我们无法区分xy,因为无法区分std::get<0>(__e)std::get<1>(__e)所做的事情:两者都返回一个int&.

这也是增加上述情况与正常结构情况之间一致性的方法:

struct C {
    int i;
    int& r;
};
C& bar();
auto& [a, b] = bar();

出于结构化绑定的目的,我们希望此处的 ab 与此处的 xy 的行为方式相同。而这里的ab并不是引入变量,只是__e.i__e.r的不同名称。


在非参考情况下,有一个我们无法区分的不同场景:

std::tuple<int, int&&> foo();
auto [x, y] = foo();

这里,我们目前通过以下方式解压:

auto __e = foo();
std::tuple_element_t<0, decltype(e)>& x = std::get<0>(std::move(__e));
std::tuple_element_t<1, decltype(e)>& y = std::get<1>(std::move(__e));

两者 std::get 都调用 return 和 int&&,因此您无法使用 auto&& 来区分它们...但是 tuple_element_t 的结果不同 - intint&&。这种差异 也可以 在正常的结构案例中看到。


请注意,由于 CWG 2313,实际上解包发生在一个唯一命名的变量引用中,绑定中指定的标识符仅引用这些对象。