变量之--列表初始化和结构化绑定
变量之(变长)列表初始化
关键词
std::initializer_list
解释
在 C++98/03 中的对象初始化方法有很多种,这无疑增大了学习难度。这中情况在C++11中终于得到解决。
先看看没有C++11的时候
//初始化列表
int i_arr[3] = { 1, 2, 3 }; //普通数组
struct A
{
int x;
struct B
{
int i;
int j;
} b;
} a = { 1, { 2, 3 } }; //POD类型
//拷贝初始化(copy-initialization)
int i = 0;
class Foo
{
public:
Foo(int) {}
} foo = 123; //需要拷贝构造函数
//直接初始化(direct-initialization)
int j(0);
Foo bar(123);
int data = 0; //赋值初始化
int data = {0}; //花括号初始化
int data(0); //构造初始化
int data{0}; //花括号初始化
这些不同的初始化方法,都有各自的适用范围和作用。最关键的是,这些种类繁多的初始化方法,没有一种可以通用所有情况。
为了统一初始化方式,并且让初始化行为具有确定的效果,C++11 中提出了列表初始化(List-initialization)的概念。
统一的初始化
举个小例子看看C++11变长初始化列表是怎么用的
#include <initializer_list>
class MagicFoo{
public:
std::vector<int> vec;
MagicFoo(std::initializer_list<int> list){
for (std::initializer_list<int>::iterator it = list.begin();
it!= list.end(); ++it)
vec.push_back(*it);
}
};
int main(){
// after C++11
MagicFoo magicFoo = {1, 2, 3, 4, 5};
for (auto it : magicFoo.vec)
std::cout << it << std::endl;
}
这里需要注意的是,虽然使用了等于号,但它仍然是列表初始化,因此,私有的拷贝构造并不会影响到它。
总结
C++11统一了所有初始化方式,都变成了下面的形式:
Foo a3 = { 123 };
Foo a4 { 123 };
除了上面所述的内容之外,列表初始化还可以直接使用在函数的返回值上:
struct Foo
{
Foo(int, double) {}
};
Foo func(void)
{
return { 123, 321.0 };
}
这里的 return 语句就如同返回了一个 Foo(123, 321.0)。
结构化绑定
简介
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 左值
变量之--列表初始化和结构化绑定相关推荐
- 【C++grammar】结构化绑定
目录 定义 1.用于原生数组的结构化绑定声明 2.用于std::array的结构化绑定声明 3.用于对象数据成员的结构化绑定声明 定义 结构化绑定声明是一个声明语句,意味着声明了一些标识符并对标识符做 ...
- c++17 to_chars、from_chars、if、结构化绑定
c++17 to_chars.from_chars.if.结构化绑定 说明 std::to_chars std::from_chars 示例代码 输出 参考 说明 std::to_chars 通过成功 ...
- C2429 语言功能“结构化绑定“需要编译器标志“/std:c++17“
C2429 语言功能"结构化绑定"需要编译器标志"/std:c++17" 1.问题 vs2019打开工程时报错:C2429 语言功能"结构化绑定&qu ...
- 列表初始化和赋值初始化的使用注意事项
成员变量初始化方式有两种:列表初始化和赋值初始化.如下代码.但是这两种初始化表面上看着相同,但是用法和原理却并不相同.本篇博客主要讨论这两种初始化的使用方法和基本原理. class Test {pub ...
- 结构化、非结构化和半结构化数据 数据清洗
结构化数据 ** 结构化数据可以使用关系型数据库来表示和存储,如MySQL.Oracle.SQL Server等,表现二维形式的数据.可以通过固有键值获取相应信息.一般特点是:数据以行为单位,一行数 ...
- 现代c++之列表初始化/统一初始化
#include <map> #include <string>class CDemo { public:int x;int y; };int main(void) {int ...
- 结构化数据、半结构化数据、非结构化数据
1 概念 结构化数据 结构化数据可以使用关系型数据库来表示和存储,如MySQL.Oracle.SQL Server等,表现二维形式的数据.可以通过固有键值获取相应信息. 一般特点是:数据以行为单位,一 ...
- 结构化、非结构化和半结构化数据
一直对结构化.非结构化和半结构化数据三个数据类型的概念有点模糊不清,今天特意花点时间网上查找了一番,继而,来此处进行一通总结. ** 结构化数据 ** 结构化数据可以使用关系型数据库来表示和存储,如M ...
- C语言中结构化数据(变量,指针,数组,字符串,结构体和联合)的内存表示
目录 结构化数据表示 一.内存地址: 二.全局变量和局部变量的内存布局 三.数据在内存中的表示 四.数组在内存中的表示 五.字符串在内存中的表示 六.结构和联合在内存中的表示 结构化数据表示 一.内存 ...
最新文章
- 《Redis设计与实现》之第七章:压缩列表
- 【cmd】日期、时间格式化
- helm安装postgres_Helm 入门介绍 Kubernetes 上的包管理软件
- Android Ubuntu 安装问题FAQ
- vba打开txt文件_VBA基础入门(34)读取txt文本文件
- Python可以这样学(第三季:多线程与多进程编程)-董付国-专题视频课程
- 强命名保护DLL文件
- 6.11 如何在Excel自选图形中显示公式的数值 [原创Excel教程]
- Janusgraph索引
- 鸿蒙蕴含的哲理,苏轼最不该被忽视哲理名句:“人生到处知何似,应似飞鸿踏雪泥”...
- 安装chrome插件:FireShot
- JS去除输入文本的所有空格、逗号、换行符等空白字符
- 计算机品牌及介绍,【推广】电脑电源参数和品牌的介绍
- 如何删除计算机桌面多余的大e,教你删除属性里桌面多余背景图片
- OO第二单元电梯作业总结
- transformer模型的奥秘-学习笔记
- 地下水动力学--地下水水文学(持续更新)
- 杰理之KeyPage【篇】
- 爬虫(20)Scrapy知识补充+腾讯招聘案例+古诗文详情页+总结
- 写给电子工程师的,非常值得一看
热门文章
- uva 11995 I Can Guess the Data Structure!
- C\C++ 位域操作
- 什么叫「人的格局」?是否有必要培养大的格局或怎么培养?
- 牛客网(剑指offer) 第十九题 顺时针打印矩阵
- iOS之如何实现isEqual的重写
- LeetCode Algorithm 面试题 16.10. 生存人数
- Spark is not running in local mode, therefore the checkpoint directory must not be on the local……
- 教小学妹学算法:诺基亚引出的动态规划问题
- 深度学习——02、深度学习入门——卷积神经网络
- 大数据WEB阶段(十六)JavaEE三大 核心技术之监听器Listener