从C++ 14到C++ 17:理解聚合初始化是如何工作的

C++ 17中的扩展聚合初始化

  • 一、引言
  • 二、C++ 14中的代码
  • 三、C++ 17中的代码
  • 四、扩展聚合初始化
  • 五、为什么代码停止编译?
  • 六、总结

一、引言

将编译器升级到C++ 17,某些看起来合理的代码停止了编译。这段代码没有使用任何在C++ 17中删除的过时特性,如std::auto_ptrstd::bind1st,但它仍然停止编译。理解这个编译错误将能更好地理解C++ 17的一个新特性:扩展聚合初始化

二、C++ 14中的代码

一个示例代码:

template<typename Derived>
struct Base
{
private:Base(){};friend Derived;
};struct Derived : Base<Derived>
{
};int main()
{Derived d{};
}

这段代码是与CRTP(Curiously Recurring Template Pattern)相关的经典技巧,以避免将错误的类传递给CRTP基类。在C++ 14中,上面的代码可以编译,但是稍微修改一下,其中CRTP派生类不将自身作为模板形参传递给基类,即使在C++ 14中也无法编译:

template<typename Derived>
struct Base
{
private:Base(){};friend Derived;
};struct X{};struct Derived : Base<X> // passing the wrong class here
{
};int main()
{Derived d{};
}

当试图构造Derived时,它需要调用基类base的构造函数,但后者是私有的,并且只与模板形参为friend。模板参数必须是Derived才能编译代码。

在c++ 14中,第一个版本编译得很好。下面是C++ 14中第二种情况的编译错误:

<source>: In function 'int main()':
<source>:17:15: error: use of deleted function 'Derived::Derived()'17 |     Derived d{};|               ^
<source>:11:8: note: 'Derived::Derived()' is implicitly deleted because the default definition would be ill-formed:11 | struct Derived : Base<X>|        ^~~~~~~
<source>:11:8: error: 'Base<Derived>::Base() [with Derived = X]' is private within this context
<source>:5:5: note: declared private here5 |     Base(){};|     ^~~~
Compiler returned: 1

三、C++ 17中的代码

继续看一下C++ 14中编译的第一个正确版本:

template<typename Derived>
struct Base
{
private:Base(){};friend Derived;
};struct Derived : Base<Derived>
{
};int main()
{Derived d{};
}

如果尝试用C++ 17编译它,会得到以下错误:

<source>: In function 'int main()':
<source>:15:15: error: 'Base<Derived>::Base() [with Derived = Derived]' is private within this context15 |     Derived d{};|               ^
<source>:5:5: note: declared private here5 |     Base(){};|     ^~~~

Base仍然是Derivefriend,为什么编译器不会接受构造一个Derived对象?

四、扩展聚合初始化

好,让我们看看这里发生了什么。

c++ 17带来的特性之一是扩展了聚合初始化。

聚合初始化是指调用点通过初始化其成员而不使用显式定义的构造函数来构造对象。示例:

struct X
{int a;int b;int c;
};

然后可以用下面的方法构造X

X x{1, 2, 3};

调用时用1、2和3初始化a、b和c,不需要x的任何构造函数。这是C++ 11开始允许的。

但是,实现这一特性的规则非常严格:类不能有私有成员、基类、虚函数和许多其他东西。

在C++ 17中,其中一条规则得到了放宽:即使类有基类,也可以执行聚合初始化。不过,调用时必须初始化基类。示例:

struct X
{int a;int b;int c;
};struct Y : X
{int d;
};

Y继承自X。在C++ 14中,这使Y无法进行聚合初始化。但是在c++ 17中,可以这样构造一个Y:

Y y{1, 2, 3, 4};
// or
Y y{ {1, 2, 3}, 4};

两种语法分别将a、b、c和d初始化为1、2、3和4。

也可以这样写:

Y y{ {}, 4 };

这将a, b和c初始化为0,d初始化为4。

但是,要注意,这并不等同于这个:

Y y{4};

因为这将a(而不是d)初始化为4,将b, c和d初始化为0。也可以在X中指定部分属性:

Y y{ {1}, 4};

这将a初始化为1,b和c初始化为0,d初始化为4。

现在已经熟悉了扩展聚合初始化,让我们回到初始代码。

五、为什么代码停止编译?

下面的代码在C++ 14中编译良好,在C++ 17中停止编译:

template<typename Derived>
struct Base
{
private:Base(){};friend Derived;
};struct Derived : Base<Derived>
{
};int main()
{Derived d{};
}

注意到调用Derived构造的大括号了吗?在C++ 17中,它们触发聚合初始化,并尝试实例化具有私有构造函数的Base。这就是它停止编译的原因。

构造函数的调用位置是构造基类,而不是构造函数本身。如果修改基类,使其与构造函数的调用位置为friend,则代码在C++ 17中也可以很好地编译:

template<typename Derived>
struct Base
{
private:Base(){};friend int main(); // this makes the code compile
};struct Derived : Base<Derived>
{
};int main()
{Derived d{};
}

当然,肯定不打算这样写代码,每个调用点都有一个friend是不合理的!这个更改只是为了说明调用点直接调用基类的构造函数这一事实。

要修复代码,可以……去掉括号(哈哈哈哈):

template<typename Derived>
struct Base
{
private:Base(){};friend Derived;
};struct Derived : Base<Derived>
{
};int main()
{Derived d;
}

它又可以编译了。

注意,这时已不再从值初始化中获益了。如果Derivedclass包含数据成员,需要确保在显式声明的构造函数中或在类中声明这些成员时初始化它们。

这个例子让我们更好地理解聚合初始化是如何工作的,以及它在C++ 17中是如何变化的。删除两个字符能教会我们多少东西,是不是非常有趣!

六、总结

本文详细介绍了C++ 17中的扩展聚合初始化特性,该特性使得初始化聚合类型的对象变得更加简洁和灵活。通过对比C++ 14中的代码,发现在C++ 17中引入的扩展聚合初始化语法可以大大简化代码,并提供了更好的可读性和可维护性。也指出了一些在C++ 17中代码停止编译的情况,并解释了其中的原因。通过深入理解扩展聚合初始化的语法和语义,可以在自己的项目中充分利用这一特性,提升代码的效率和可靠性。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

03.卸载MySQL

卸载MySQL 1.Windows卸载MySQL8 停止服务 用命令停止或者在服务中停止都可以 net stop mysql&#xff08;服务名字可以去服务里面看一下&#xff09;控制面板卸载MySQL 卸载MySQL8.0的程序可以和其他桌面应用程序一样直接在控制面板选择卸载程序&#xff0c;并在程序列表中…

OpenHarmony、HarmonyOS和Harmony NEXT 《我们不一样》

1. OpenHarmony 定义与地位&#xff1a;OpenHarmony是鸿蒙系统的底层内核系统&#xff0c;集成了Linux内核和LiteOS&#xff0c;为各种设备提供统一的操作系统解决方案。 开源与商用&#xff1a;OpenHarmony是一个开源项目&#xff0c;允许开发者自由访问和使用其源代码&#…

基于afx透明视频的视觉增强前端方案

作者 | 青玉 导读 本文介绍了增长前端团队自研的Webview框架下透明视频视觉增强方案&#xff0c;该方案在保证对视觉进行高度还原的同时可投入更少的开发成本&#xff0c;还能获得更优的前端性能表现。文章首先分析了市面上动画方案的优缺点&#xff0c;然后详细介绍了透明视频…

计算机视觉——OpenCV Python基于颜色识别的目标检测

1. 计算机视觉中的颜色空间 颜色空间在计算机视觉领域的应用非常广泛&#xff0c;它们在图像和视频处理、物体检测等任务中扮演着重要角色。颜色空间的主要作用是将颜色以数值形式表示出来&#xff0c;这样计算机算法就能够对其进行处理和分析。不同的颜色空间有着不同的特点和…

agi入门-大模型开发基础

AGI(Artifical General Inteligence)的到来还有多久&#xff1f; 乐观预测&#xff1a;明年主流预测&#xff1a;3-5年悲观预测&#xff1a;10年 AGI时代&#xff0c;AI无处不在&#xff0c;相关从来者将如何分&#xff1f; AI使用者&#xff1a;使用别人开发的AI产品AI产品…

中国人工智能产业年会智能交通与自动驾驶专题全景扫描

中国人工智能产业年会&#xff08;CAIIAC&#xff09;是中国人工智能技术发展和应用的重要展示平台&#xff0c;不仅关注创新&#xff0c;还涵盖了市场和监管方面的内容&#xff0c;对于促进人工智能领域的发展起到了重要作用。年会汇集了来自学术界、工业界和政府的专家&#…

【二分查找】Leetcode 74. 搜索二维矩阵【中等】

搜索二维矩阵 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c…

【InternLM 实战营第二期笔记】LMDeploy 量化部署 LLMVLM实战

Huggingface与TurboMind介绍 Huggingface HuggingFace是一个高速发展的社区&#xff0c;包括Meta、Google、Microsoft、Amazon在内的超过5000家组织机构在为HuggingFace开源社区贡献代码、数据集和模型。可以认为是一个针对深度学习模型和数据集的在线托管社区&#xff0c;如…

4.Labview簇、变体与类(上)

在Labview中&#xff0c;何为簇与变体&#xff0c;何为类&#xff1f;应该如何理解&#xff1f;具体有什么应用场景&#xff1f; 本文基于Labview软件&#xff0c;独到的讲解了簇与变体与类函数的使用方法和场景&#xff0c;从理论上讲解其数据流的底层概念&#xff0c;从实践上…

【学习笔记】Python大数据处理与分析——pandas数据分析

一、pandas中的对象 1、Series对象 由两个相互关联的数组(values, index)组成&#xff0c;前者&#xff08;又称主数组&#xff09;存储数据&#xff0c;后者存储values内每个元素对应关联的标签。 import numpy as np import pandas as pds1 pd.Series([1, 3, 5, 7])print(…

xxl-job使用自动注册节点,ip不对,如何解决????

很明显这时我们本机的ip和我们xxl-job自动注册的ip是不一致的&#xff0c;此时该如何处理呢&#xff1f;&#xff1f;&#xff1f;&#xff1f; 方法一&#xff1a;在配置文件中&#xff0c;将我们的ip固定写好。 ### xxl-job executor server-info xxl.job.executor.ip写你的…

Flink SQL

文章目录 一、Flink SQL1、sql-client准备1.1 基于yarn-session模式1.2 常用配置 2、流处理中的表2.1 动态表和持续查询2.2 将流转换成动态表2.3 用SQL持续查询2.4 将动态表转换为流 3、时间属性3.1 事件时间3.2 处理时间 4、DDL&#xff08;Data Definition Language&#xff…

详解UART通信协议以及FPGA实现

文章目录 一、UART概述二、UART协议帧格式2.1 波特率2.2 奇校验ODD2.3 偶校验EVEN 三、UART接收器设计3.1 接收时序图3.2 Verilog代码3.3 仿真文件测试3.4 仿真结果3.5 上版测试 四、UART发送器设计4.1 发送时序图4.2 Verilog代码4.3 仿真文件测试4.4 仿真结果4.5 上板测试 五、…

【Web】Dest0g3 520迎新赛 题解(全)

目录 phpdest EasyPHP SimpleRCE funny_upload EasySSTI middle PharPOP ezip NodeSoEasy Really Easy SQL&easysql EzSerial ljctr phpdest 尝试打pearcmd&#xff0c;但似乎没有写文件的权限 ?config-create/&file/usr/local/lib/php/pearcmd.php&a…

从零开始写 Docker(十一)---实现 mydocker exec 进入容器内部

本文为从零开始写 Docker 系列第十一篇&#xff0c;实现类似 docker exec 的功能&#xff0c;使得我们能够进入到指定容器内部。 完整代码见&#xff1a;https://github.com/lixd/mydocker 欢迎 Star 推荐阅读以下文章对 docker 基本实现有一个大致认识&#xff1a; 核心原理&…

STM32 F103 C8T6开发笔记14:与HLK-LD303-24G测距雷达通信

今日尝试配通STM32 F103 ZET6与HLK-LD303-24G测距雷达的串口通信解码 文章提供测试代码...... 目录 HLK-LD303-24G测距雷达外观&#xff1a; 线路连接准备&#xff1a; 定时器与串口配置准备&#xff1a; 定时器2的初始化&#xff1a; 串口1、2初始化&#xff1a; 串口1、2自定…

C++从入门到精通——类和对象(下篇)

1. 再谈构造函数 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;} private:int _year;int _mont…

Web3.0与AI的交融:开启智能互联网新时代

目前有140 多个 Web3 AI 概念项目&#xff0c;覆盖了基础设施、数据、预测市场、计算与算力、教育、DeFi & 跨链、安全、NFT & 游戏 & 元宇宙、搜索引擎、社交 & 创作者经济、AI 聊天机器人、DID & 消息传递、治理、医疗、交易机器人等诸多方向。持续关注…

C++笔记:类和对象

类和对象 认识类和对象 先来回忆一下C语言中的类型和变量&#xff0c;类型就像是定义了数据的规则&#xff0c;而变量则是根据这些规则来实际存储数据的容器。类是我们自己定义的一种数据类型&#xff0c;而对象则是这种数据类型的一个具体实例。类就可以理解为类型&#xff0c…

怎么用手机远程控制电脑 远程控制怎么用

怎么用手机远程控制电脑&#xff1a;远程控制怎么用 在这个科技日新月异的时代&#xff0c;远程控制电脑已经成为了很多人的需求。有时&#xff0c;我们可能在外出时突然需要访问家中的电脑&#xff0c;或者在工作中需要远程操控办公室的电脑。这时&#xff0c;如果能用手机远…