c++ - 不同的隐式生成函数之间是否存在可观察到的语义差异?
问题描述
我一直在阅读 C++ 标准,试图了解琐碎、简单和隐式定义的构造函数/赋值运算符/析构函数之间是否存在任何可观察到的差异。根据我目前的理解,似乎没有区别,但这似乎很奇怪,为什么要花这么多时间来定义它们并不重要?
作为一个具体的例子,考虑复制构造函数。
- 如果所有字段和基类都是平凡的,那么平凡的复制构造函数会逐个字段地复制所有字段和基类。
- 否则,隐式生成的复制构造函数:“按初始化顺序执行基和非静态成员的完整成员副本”。
如果我理解正确,如果一个类具有所有平凡的基础和字段,但具有默认的复制构造函数,那么默认的复制构造函数将与平凡构造函数完全相同。甚至初始化顺序在这里似乎都无关紧要,因为这些字段都是不相交的(因为琐碎意味着没有virtual
基类)。
有没有一个例子,一个简单的复制构造函数会做一些不同于显式默认的复制构造函数的事情?
通常,相同的逻辑似乎也适用于其他构造函数和析构函数。由于数据竞争的可能性,分配的论点有点复杂,但如果类实际上是微不足道的,那么所有这些似乎都是标准未定义的行为。
解决方案
不完全关于实际特殊成员函数本身的行为*,但请考虑以下几点:
struct Normal
{
int a;
};
static_assert(std::is_trivially_move_constructible_v<Normal>);
static_assert(std::is_trivially_copy_constructible_v<Normal>);
static_assert(std::is_copy_constructible_v<Normal>);
这一切似乎都很好。
现在考虑以下几点:
struct Strange
{
Strange() = default;
Strange(Strange&&) = default;
};
static_assert(std::is_trivially_move_constructible_v<Strange>);
static_assert(!std::is_trivially_copy_constructible_v<Strange>);
static_assert(!std::is_copy_constructible_v<Strange>);
唔。仅仅显式默认移动构造函数的行为就不允许对象是可复制构造的!
为什么是这样?
因为,即使编译器仍在为定义移动构造函数Strange
,它仍然是用户声明的移动构造函数,它禁用了复制特殊成员函数的生成。
当您拥有用户声明的版本时,该标准对生成哪些特殊成员函数非常挑剔,因此最好坚持五或零规则。
额外学分
通过显式默认默认构造函数Strange
,它不再是聚合类型(而Normal
现在是)。这打开了一个完全不同的关于初始化的蠕虫罐。
*因为据我所知,显式默认的特殊成员函数的行为与该函数的普通版本相同(或者更确切地说,它是相反的)。但是,我必须指出标准措辞的一个特点;在讨论隐式声明的复制构造函数时,标准忽略了“隐式声明为默认”,就像它对默认和移动构造函数所做的那样。我相信这是一个小错误。
推荐阅读
- python - 如何在 ib_insync 中获取我的股份数量?
- javascript - Vue.js - 注入一个 v-on 按钮以从来自外部源的静态 html 生成覆盖
- spring - @WebMvcTest - 如何配置安全性以便测试可以运行?
- python - 当我尝试将 sql 表加载到数据框中时,显示数据框中字符串的 NAN 值
- powershell - Azure DevOps 服务器 Invoke-RestMethod 错误:没有为“PUT”请求提供 API 版本
- amazon-web-services - 有没有一种简单的方法来克隆粘合作业,但更改数据库连接?
- python - 如何根据python中的条件从字典中的列表中删除元素
- react-native-push-notification - 当应用程序不工作时,PushNotificationIOS.getInitialNotification() 总是返回 null
- json - 如何替换存储在 JSON 文件中的 JSON 值并在 Rest Assured 测试中使用它
- javascript - XMLhttprequest 不发布(新的)