为什么结构化绑定依赖于 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
和成员 get
或 std::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
的另一个名称,而 y
是 foo().__1
的另一个名称,这意味着它们应该是 int
和 int&
。按照今天的具体说明,这将解压缩为 †:
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);
那么我们无法区分x
和y
,因为无法区分std::get<0>(__e)
和std::get<1>(__e)
所做的事情:两者都返回一个int&
.
这也是增加上述情况与正常结构情况之间一致性的方法:
struct C {
int i;
int& r;
};
C& bar();
auto& [a, b] = bar();
出于结构化绑定的目的,我们希望此处的 a
和 b
与此处的 x
和 y
的行为方式相同。而这里的a
和b
并不是引入变量,只是__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
的结果不同 - int
和 int&&
。这种差异 也可以 在正常的结构案例中看到。
†请注意,由于 CWG 2313,实际上解包发生在一个唯一命名的变量引用中,绑定中指定的标识符仅引用这些对象。
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
和成员 get
或 std::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
的另一个名称,而 y
是 foo().__1
的另一个名称,这意味着它们应该是 int
和 int&
。按照今天的具体说明,这将解压缩为 †:
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);
那么我们无法区分x
和y
,因为无法区分std::get<0>(__e)
和std::get<1>(__e)
所做的事情:两者都返回一个int&
.
这也是增加上述情况与正常结构情况之间一致性的方法:
struct C {
int i;
int& r;
};
C& bar();
auto& [a, b] = bar();
出于结构化绑定的目的,我们希望此处的 a
和 b
与此处的 x
和 y
的行为方式相同。而这里的a
和b
并不是引入变量,只是__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
的结果不同 - int
和 int&&
。这种差异 也可以 在正常的结构案例中看到。
†请注意,由于 CWG 2313,实际上解包发生在一个唯一命名的变量引用中,绑定中指定的标识符仅引用这些对象。