其实C++11结构化绑定做的并不好,想要用结构化绑定的更好的版本要等到C++17。所以这里会提到C++17的用法。
简介
C++17语言上(语言特性,而不是标准库新特性)引入了一种结构化绑定的新特性,使用该特性可以利用auto同时声明多个不同类型的变量并即时从一个tuple-like对象得到赋值/初始化。
Structured binding不但可以使C++的代码更加简洁,而且似乎从语法上更贴近Python这种脚本语言了。另外,auto变量会在编译时推导出变量的类型,所以无需担心会有运行时效率的下降。而且,好像也并不会影响到编译效率,这一点尚未看到有实测。
在C++11的时候,如果要接收从函数返回的std::tuple
对象,我们可以使用std::tie
。
举个例子:
#include <iostream>
#include <tuple>
std::tuple<int, double, std::string> f(){
return std::make_tuple(1, 2.3, "456");
}
int main(){
// C++17
auto [x, y, z] = f();
std::cout << x << ", " << y << ", " << z << std::endl;
//C++11
int a; double b, std::string c;
std::tie(a, b, c) = f();std::cout << a << ", " << b << ", " << c << std::endl;
return 0;
}
结构化绑定声明 (C++17 起)
下面部分可以忽略。。。只是为了解释C++17的结构化绑定。
绑定指定名称到初始化器的子对象或元素。
类似引用,结构化绑定是既存对象的别名。不同于引用的是,结构化绑定的类型不必为引用类型。
定义
attr(可选) cv-autoref-运算符(可选) [ 标识符列表 ] = 表达式 ; (1)
attr(可选) cv-autoref-运算符(可选) [ 标识符列表 ] { 表达式 } ; (2)
attr(可选) cv-autoref-运算符(可选) [ 标识符列表 ] ( 表达式 ) ; (3)
attr - 任意数量的属性的序列
cv-auto - 可有 cv 限定的 auto 类型说明符,亦可包含存储类说明符 static 或 thread_local ;在 cv 限定符中包含 volatile 是被弃用的 (C++20 起)
ref-运算符 - &或 && 之一
标识符列表 - 此声明所引入的各标识符的逗号分隔的列表
表达式 - 顶层没有逗号运算符的表达式(文法上为赋值表达式),且具有数组或非联合类之一的类型。若表达式 涉及任何来自 标识符列表 的名字,则声明非良构。
情况 1:绑定数组
标识符列表 中的每个标识符均成为指代数组的对应元素的左值。标识符的数量必须等于数组的元素数量。
每个标识符的被引用类型都是数组的元素类型。注意若数组类型 E
为 cv 限定的,则其元素亦然。
int a[2] = {1,2};
auto [x,y] = a; // 创建 e[2],复制 a 到 e,然后 x 指代 e[0],y 指代 e[1]
auto& [xr, yr] = a; // xr 指代 a[0],yr 指代 a[1]
情况 2:绑定元组式类型
表达式 std::tuple_size<E>::value 必须是良构的整数常量表达式,且标识符的数量必须等于 std::tuple_size<E>::value。
对于每个标识符,引入一个类型为“std::tuple_element<i, E>::type 的引用”的变量:若其对应初始化器是左值,则为左值引用,否则为右值引用。第 i 个变量的初始化器
-
若在
E
的作用域中对标识符get
按类成员访问进行的查找中,至少找到一个声明是首个模板形参为非类型形参的函数模板,则为 e.get<i>() -
否则为 get<i>(e),其中 get 只进行实参依赖查找,忽略非 ADL 的查找。
这些初始化器表达式中,若实体 e
的类型为左值引用(这仅在 ref-运算符 为 &
,或为 &&
且初始化器为左值时才发生),则 e 为左值,否则为亡值(这实际上进行了一种完美转发),i 是 std::size_t 的纯右值,而且始终将 <i> 解释为模板形参列表。
变量拥有与 e
相同的存储期。
然后该标识符变成指代与上述变量绑定的对象的左值。
第 i 个标识符的被引用类型为 std::tuple_element<i, E>::type。
float x{};
char y{};
int z{};
std::tuple<float&,char&&,int> tpl(x,std::move(y),z);
const auto& [a,b,c] = tpl;
// a 指名指代 x 的结构化绑定;decltype(a) 为 float&
// b 指名指代 y 的结构化绑定;decltype(b) 为 char&&
// c 指名指代 tpl 的第 3 元素的结构化绑定;decltype(c) 为 const int
情况 3:绑定到数据成员
E
的所有非静态数据成员必须都是 E
或 E
的同一基类的直接成员,必须在指名为 e.name
时于结构化绑定的语境中是良构的。E
不能有匿名联合体成员。标识符的数量必须等于非静态数据成员的数量。
标识符列表 中的各个标识符,按声明顺序依次成为指代 e
的各个成员的左值的名字(支持位域);左值的类型是 cv T_i
,其中 cv
是 E
的 cv 限定符且 T_i
是第 i 个成员的声明类型。
第 i 个标识符的被引用类型是 cv T_i
。
struct S {
int x1 : 2;
volatile double y1;
};
S f();
const auto [x, y] = f(); // x 是标识 2 位位域的 const int 左值
// y 是 const volatile double 左值
关注公众号获取更多信息: