我一直在阅读 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
的移动构造函数,它仍然是 用户声明 移动构造函数,它禁用复制特殊成员函数的生成。当您拥有用户声明版本的特殊成员函数时,标准非常挑剔会生成哪些特殊成员函数,因此最好坚持使用 Rule of Five or Zero .
Live Demo
额外学分
通过显式默认
Strange
的默认构造函数,它不再是聚合类型(而 Normal
是)。这开启了一个完全不同的关于初始化的蠕虫。*因为据我所知,显式默认的特殊成员函数的行为与该函数的普通版本(或者更确切地说, it's the other way around )相同。但是,我必须注意标准措辞的一个特点;在讨论隐式声明的复制构造函数时,标准忽略了“隐式声明为默认值”,就像它对默认和移动构造函数所做的那样。我相信这是一个小错误。
关于c++ - 不同的隐式生成函数之间是否存在可观察到的语义差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63608199/