C++17新特性 结构化绑定

news2024/11/24 3:14:10

一、Python中的相似功能

熟悉python的应该对下面的代码很熟悉

def return_multiple_values():
    return 11, 7

x, y = return_multiple_values()

函数返回一个元组,元组自动分配给了x和y。

二、C++11中的元组

c++11中就存在类似python元组的概念了:

std::tuple<int, int> return_multiple_values() {
    return std::make_tuple(11, 7);
}

int main() {
    int x = 0, y = 0;
    std::tie(x, y) = return_multiple_values();
    std::cout << "x=" << x << " y=" << y << std::endl;
}

这段代码和Python完成了同样的工作,但代码却要麻烦许多。一个原因是C++11必须指定return_multiple_values函数的返回值类型,另外,在调用return_multiple_values函数前还需要声明变量xy,并且使用函数模板std::tiexy通过引用绑定到std::tuple<int&, int&>

三、结构化绑定

对于第一个问题,可以使用C++14中auto的新特性来简化返回类型的声明

auto return_multiple_values() {
    return std::make_tuple(11, 7);
}

对于第二个问题,就必须使用C++17的新特性,结构化绑定。所谓结构化绑定是指将一个或者多个名称绑定到初始化对象中的一个或者多个子对象(或者元素)上,相当于给初始化对象的子对象(或者元素)起了别名,请注意别名不同于引用。

auto return_multiple_values() {
    return std::make_tuple(11, 7);
}

int main() {
    auto[x, y] = return_multiple_values();
    std::cout << "x=" << x << " y=" << y << std::endl;
}

其中auto是类型占位符,[x, y]是绑定标识符列表,其中xy是用于绑定的名称,绑定的目标是函数return_multiple_values()返回结果副本的子对象或者元素。

结构化绑定的目标不必是一个函数的返回结果,实际上等号的右边可以是任意一个合理的表达式,比如:

struct BindTest {
    int a = 42;
    std::string b = "hello structured binding";
};

int main() {
    BindTest bt;
    auto[x, y] = bt;
    std::cout << "x=" << x << " y=" << y << std::endl;
}

可以看到结构化绑定能够直接绑定到结构体上。将其运用到基于范围的for循环中会有更好的效果:

struct BindTest {
    int a = 42;
    std::string b = "hello structured binding";
};

int main() {
    std::vector<BindTest> bt{ {11, "hello"},  {7, "c++"},  {42, "world"} };
    for (const auto& [x, y] : bt) {
       std::cout << "x=" << x << " y=" << y << std::endl;
    }
}

四、深入理解结构化绑定

在结构化绑定时,编译器会生成一个等号右边对象的匿名副本,绑定的对象是这个匿名副本,不是右边对象,或者右边对象的引用。

BindTest bt;
const auto [x, y] = bt;

// 经过编译器处理
BindTest bt;
const auto _anonymous = bt;
aliasname x = _anonymous.a
aliasname y = _anonymous.b

_anonymous是编译器生成的匿名对象,const auto [x, y] = btauto的限定符会直接应用到匿名对象_anonymous上。也就是说,_anonymousconst还是volatile完全依赖auto的限定符。

4.1、示例一

看下面的代码:

int main() {
  BindTest bt;
  const auto[x, y] = bt;

  std::cout << "&bt.a=" << &bt.a << " &x=" << &x << std::endl;
  std::cout << "&bt.b=" << &bt.b << " &y=" << &y << std::endl;
  std::cout << "std::is_same_v<const int, decltype(x)>=" 
       << std::is_same_v<const int, decltype(x)> << std::endl;
  std::cout << "std::is_same_v<const std::string, decltype(y)>=" 
       << std::is_same_v<const std::string, decltype(y)> << std::endl;
}

结果如下

image-193526431

可以看到别名x并不是bt.a,因为它们的内存地址不同。另外,xy的类型分别与const intconst std::string相同也证明了它们是别名而不是引用的事实。可见,如果在上面这段代码中试图使用xy去修改bt的数据成员是无法成功的,因为一方面xy都是常量类型;另一方面即使xy是非常量类型,改变的xy只会影响匿名对象而非bt本身。

4.2、示例二

看下面的代码

int main() {
    BindTest bt;
    auto&[x, y] = bt;

    std::cout << "&bt.a=" << &bt.a << " &x=" << &x << std::endl;
    std::cout << "&bt.b=" << &bt.b << " &y=" << &y << std::endl;

    x = 11;
    std::cout << "bt.a=" << bt.a << std::endl;
    bt.b = "hi structured binding";
    std::cout << "y=" << y << std::endl;
}

结果如下

image-20240513193859400

虽然只是将const auto修改为auto&,但是已经能达到让bt数据成员和xy相互修改的目的了。别名真的是单纯的别名,别名的类型和绑定目标对象的子对象类型相同,而引用类型本身就是一种和非引用类型不同的类型

4.3、示例三

auto t = std::make_tuple(42, "hello world");
auto [x] = t;

以上代码是无法通过编译的,必须有两个别名分别对应bt的成员变量ab,使用结构化绑定无法忽略对象的子对象或者元素。

可以仿照C++11中std::tie使用std::ignore的方案:

auto t = std::make_tuple(42, "hello world");
int x = 0, y = 0;
std::tie(x, std::ignore) = t;
std::tie(y, std::ignore) = t;

虽然这个方案对于std::tie是有效的,但是结构化绑定的别名还有一个限制:无法在同一个作用域中重复使用。这一点和变量声明是一样的,比如:

auto t = std::make_tuple(42, "hello world");
auto[x, ignore] = t;
auto[y, ignore] = t;    // 编译错误,ignore无法重复声明

五、结构化绑定的三种类型

5.1、绑定到原生数组

绑定到原生数组即将标识符列表中的别名一一绑定到原生数组对应的元素上。所需条件仅仅是要求别名的数量与数组元素的个数一致,比如:

int a[3]{ 1, 3, 5 };
auto[x, y, z] = a;
std::cout << "[x, y, z]=[" << x << ", " << y << ", " << z << "]" << std::endl;

5.2、绑定到结构体和类对象

一些限制:

1、类或者结构体中的非静态数据成员个数必须和标识符列表中的别名的个数相同;

2、这些数据成员必须是公有的

3、这些数据成员必须是在同一个类或者基类中

4、绑定的类和结构体中不能存在匿名联合体

示例一:

class BindTest {
    int a = 42;        // 私有成员变量
public:
    double b = 11.7;
};

int main() {
    BindTest bt;
    auto[x, y] = bt;   // 编译失败,有私有成员变量
    auto[x] = bt;      // 编译失败,有私有成员变量
    
}

示例二

class BindBase1 {
public:
    int a = 42;
    double b = 11.7;
};
class BindBase2 {};
class BindBase3 {
public:
    int a = 42;
};

class BindTest1 : public BindBase1 {};
class BindTest2 : public BindBase2 {
public:
    int a = 42;
    double b = 11.7;
};
class BindTest3 : public BindBase3 {
public:
    double b = 11.7;
};

int main() {
    BindTest1 bt1;
    BindTest2 bt2;
    BindTest3 bt3;
    auto[x1, y1] = bt1;    // 编译成功
    auto[x2, y2] = bt2;    // 编译成功
    auto[x3, y3] = bt3;    // 编译错误,不在一个类或者基类内
}

auto[x1, y1] = bt1auto[x2, y2] = bt2可以顺利地编译,因为类BindTest1BindTest2的非静态数据成员要么全部在派生类中定义,要么全部在基类中定义。BindTest3却不同,其中成员变量a的定义在基类,成员变量b的定义在派生类,这一点违反了绑定结构体的限制条件,所以auto[x3, y3] = bt3会导致编译错误。

5.3、绑定到元组和类元组的对象

绑定到元组就是将标识符列表中的别名分别绑定到元组对象的各个元素。绑定到类元组要从绑定的限制条件讲起。绑定元组和类元组有一系列抽象的条件:对于元组或者类元组类型T

1.需要满足std::tuple_size<T>::value是一个符合语法的表达式,并且该表达式获得的整数值与标识符列表中的别名个数相同。

2.类型T还需要保证std::tuple_element<i, T>::type也是一个符合语法的表达式,其中i是小于std::tuple_size<T>::value的整数,表达式代表了类型T中第i个元素的类型。

3.类型T必须存在合法的成员函数模板get<i>()或者函数模板get<i>(t),其中i是小于std::tuple_size<T>::value的整数,t是类型T的实例,get<i>()get<i>(t)返回的是实例t中第i个元素的值。

理解上述条件会发现,它们其实比较抽象。这些条件并没有明确规定结构化绑定的类型一定是元组,任何具有上述条件特征的类型都可以成为绑定的目标。另外,获取这些条件特征的代价也并不高,只需要为目标类型提供std::tuple_sizestd::tuple_element以及get的特化或者偏特化版本即可。实际上,标准库中除了元组本身毫无疑问地能够作为绑定目标以外,std::pairstd::array也能作为结构化绑定的目标,其原因就是它们是满足上述条件的类元组。

可以利用这个特性简化代码:

int main() {
  std::map<int, std::string> id2str{ {1, "hello"}, {3, "Structured"}, {5, "bindings"} };

    for (const auto& elem : id2str) {
        std::cout << "id=" << elem.first << ", str=" << elem.second << std::endl;
    }
}

// ==> 简化为
for (const auto&[id, str]:id2str) {
    std::cout << "id=" << id << ", str=" << str << std::endl;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1673110.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【简单介绍下Milvus】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

CV每日论文--2024.5.10

1、Attention-Driven Training-Free Efficiency Enhancement of Diffusion Models 中文标题&#xff1a;扩散模型的注意力驱动的训练免费效率增强 简介&#xff1a;扩散模型在生成高质量和多样化图像方面取得了出色的表现,但其卓越性能是以昂贵的架构设计为代价的,特别是广泛使…

激光SLAM总结——Fast LIO / Fast LIO2 / Faster LIO

激光SLAM总结——Fast LIO / Fast LIO2 / Faster LIO 在之前的工作中有接触过LOAM&#xff0c;最近在工作中又接触到Faster LIO相关的工作&#xff0c;于是想着对Fast LIO / Fast LIO2 / Faster LIO这一系列工作进行一个简单的总结&#xff0c;以加深自己对激光SLAM算法的理解…

网络网络层之(5)IPv6协议

网络网络层之(5)IPv6协议 Author: Once Day Date: 2024年5月12日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day…

未授权访问:Docker未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 4、通过crontab反弹宿主机shell 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c;还有其他大佬总结好…

【机器学习】机器学习与人工智能融合新篇章:自适应智能代理在多元化复杂环境中的创新应用与演进趋势

&#x1f512;文章目录&#xff1a; &#x1f4a5;1.引言 &#x1f68b;1.1 机器学习与人工智能的发展背景 &#x1f68c;1.2 自适应智能代理的概念与重要性 &#x1f690;1.3 研究目的与意义 ☔2.自适应智能代理的关键技术 &#x1f6e3;️2.1 环境感知与信息处理技术 …

whisper报错:hp, ht, pid, tid = _winapi.CreateProcess [WinError 2] 系统找不到指定的文件。

in _execute_child hp&#xff0c; ht&#xff0c; pid&#xff0c; tid _winapi.CreateProcess&#xff08;executable&#xff0c; args&#xff0c; FileNotFoundError&#xff1a; [WinError 2] 系统找不到指定的文件。 原因&#xff1a; 没装ffmpeg 或者 ffmpeg没添加到…

Nios实验使用串口输出“Hello Nios-II”字符到笔记本电脑

目录 实验过程 创建工程 修改程序 编译工程 运行项目 效果实现 总结 参考 实验过程 硬件设计见博主上篇博客 软件部分设计 下面使用 Nios II Software Build Tools for Eclipse 来完成当前项目的软件开发。 启动 Nios II SBT 按照下图所示点击 Nios II Software Build…

Backend - 数据分析 Pandas

目录 一、作用 二、基础环境 &#xff08;一&#xff09;执行虚拟环境的终端命令 &#xff08;二&#xff09;代码中导包 三、应用&#xff1a;一维数组 &#xff08;一&#xff09;Series对象 1. 含义 2. 常用属性和方法 &#xff08;1&#xff09;属性 &#xff08;…

springboot学习整理

视频&#xff1a;基础篇-01_springboot概述_哔哩哔哩_bilibili 介绍 spring boot 是spring提供的一个子项目&#xff0c;用于快速构建spring应用程序 spring构建&#xff1a; 1 导入依赖繁琐 &#xff1b; 2 项目配置繁琐 spring Framework: 核心 spring Boot :快速构建spring…

【LangChain学习之旅】—(21)聊天客服机器人的开发(上)

【LangChain学习之旅】—(21)聊天客服机器人的开发(上) “聊天机器人”说明项目的技术实现细节技术实现步骤简述第二步:增加记忆机制第三步:增加检索机制总结“聊天机器人”说明 聊天机器人(Chatbot)是 LLM 和 LangChain 的核心用例之一,很多人学习大语言模型,学习 …

Jmeter(三十九) - 从入门到精通进阶篇 - Jmeter配置文件的刨根问底 - 上篇(详解教程)

宏哥微信粉丝群&#xff1a;https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 为什么宏哥要对Jmeter的配置文件进行一下讲解了&#xff0c;因为有的童鞋或者小伙伴在测试中遇到一些需要修改配置文件的问题不是很清楚也不是很懂&#xff0c;就算修改了也是…

今天开发了一款软件,我竟然只用敲了一个字母(文末揭晓)

软件课题&#xff1a;Python实现打印100内数学试题软件及开发过程 一、需求管理&#xff1a; 1.实现语言&#xff1a;Python 2.打印纸张&#xff1a;A4 3.铺满整张纸 4.打包成exe 先看效果&#xff1a; 1. 2.电脑打印预览 3.打印到A4纸效果&#xff08;晚上拍的&#x…

R语言手把手教你进行支持向量机分析

1995年VAPINK 等人在统计学习理论的基础上提出了一种模式识别的新方法—支持向量机 。它根据有限的样本信息在模型的复杂性和学习能力之间寻求一种最佳折衷。 以期获得最好的泛化能力.支持向量机的理论基础决定了它最终求得的是全局最优值而不是局部极小值,从而也保证了它对未知…

云粒智慧实时数仓的架构演进分享:OceanBase + Flink CDC

4月20日&#xff0c;在2024 OceanBase开发者大会上&#xff0c;云粒智慧的高级技术专家付大伟&#xff0c;分享了云粒智慧实时数仓的构建历程。他讲述了如何在传统数仓技术框架下的相关努力后&#xff0c;选择了OceanBase Flink CDC架构的实时数仓演进历程。 业务背景及挑战 …

企业数据治理过程有哪些挑战?该如何克服?

在数字化时代&#xff0c;数据已成为企业最宝贵的资产之一。随着大数据、云计算和人工智能等技术的兴起&#xff0c;企业积累的数据量呈指数级增长。这些数据中蕴含着巨大的商业价值&#xff0c;对于推动企业发展、优化决策制定、增强市场竞争力具有不可替代的作用。然而&#…

Python-VBA函数之旅-type函数

目录 一、type函数的常见应用场景 二、type函数使用注意事项 三、如何用好type函数&#xff1f; 1、type函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a; https://myelsa1024.blog.csdn.net/ 一、type函…

企业微信hook接口协议,ipad协议http,获取群成员列表简洁版

获取群成员列表简洁版 参数名必选类型说明uuid是String每个实例的唯一标识&#xff0c;根据uuid操作具体企业微信 请求示例 {"uuid":"3240fde0-45e2-48c0-90e8-cb098d0ebe43","roomid":10696052955016166 } 返回示例 {"data": {&q…

政安晨:【Keras机器学习示例演绎】(四十四)—— 使用序列到序列转换器实现英语到西班牙语的翻译

目录 简介 设置 下载数据 解析数据 文本数据矢量化 建立模型 训练我们的模型 解码测试句子 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之…

智慧管家物业管理系统(小组项目)

目录 前言 一、项目介绍 1、目的和背景 2、项目主要内容 3、技术介绍 二、功能模块 1、重要文件结构 2、功能实现&#xff08;部分个人负责模块功能&#xff09; 2.1 展示房源信息页面 2.2 房屋详情页面 2.3 房源信息管理 三、功能模块页面 1、前台模块 2、后台…