我正在学习结构化绑定声明。我的理解是,在auto& [x, y] = expr;
中,变量x
和y
的类型是"引用std::tuple_element<i, E>::type
"(对于i=0, 1
和E
,是不可见变量e
的类型(。此外,这些变量是用get<i>(e)
初始化的。
因此,如果我使用auto&
,并且get<>
返回一个值(而不是引用(,它就不应该编译,因为您不能将左值绑定到临时值。但是,下面的例子是在GCC、Clang和Visual Studio的某些版本中为我构建的:
#include <cstddef>
#include <tuple>
#include <type_traits>
struct Foo {
template<std::size_t i>
int get() { return 123; }
};
namespace std {
template<> struct tuple_size<Foo> : integral_constant<size_t, 1> {};
template<std::size_t i> struct tuple_element<i, Foo> { using type = int; };
}
int main() {
Foo f;
auto& [x] = f;
x++;
}
此外,C++Insights清楚地表明,clang将结构化绑定扩展为:
Foo f = Foo();
Foo & __f17 = f;
std::tuple_element<0, Foo>::type x = __f17.get<0>();
x++;
这里,它声明x
不是作为引用,而是作为一个值。为什么?
我预期的是左值引用和编译错误:e
(上面例子中的__f17
(是一个左值引用。
这是因为auto&
不适用于结构化绑定。它应用于引用结构的基础实体。在您的cppnights片段中,它将是__f17
。
如果您改为使用auto [x]
,则该片段将扩展为类似于以下的内容
Foo f = Foo();
Foo __f17 = f; // Difference here
std::tuple_element<0, Foo>::type x = __f17.get<0>();
x++;
绑定本身始终是对底层对象的一种引用。然而,cppnights代码并不能准确地表示这一点。C++标准中的相关段落说这个
[dcl.struct.bind]
3否则,如果限定id
std::tuple_size<E>
命名完整类型,表达式std::tuple_size<E>::value
应是一个形式良好的积分常数表达式标识符列表中的元素应等于表示在E
的范围内查找不合格的idget
类成员访问查找,如果查找到至少一个声明,初始值设定项为e.get<i>()
。否则初始值设定项是get<i>(e)
,其中在关联的命名空间。在任何一种情况下,get<i>
都被解释为模板id。[注意:不执行普通的不合格查找。—尾注]在里面无论哪种情况,如果实体类型e
为左值,则e
为左值引用和x值。给定由指定的类型Ti
std::tuple_element<i, E>::type
,每个vi
是类型的变量使用初始值设定项初始化的"对Ti
的引用",其中如果初始值设定项是左值,并且否则为右值引用;所引用的类型是CCD_ 30。