在下面的代码中,为什么POD被认为是POD,而notPOD被认为不是POD?它们之间唯一的区别是默认构造函数的定义是放在类的内部还是外部。我一直以为两者都是一样的,但事实并非如此。
#include <iostream>
struct POD {
POD() = default;
};
struct notPOD {
notPOD();
};
notPOD::notPOD() = default;
int main() {
std::cout << std::boolalpha << std::is_pod<POD>() << ' ' << std::is_pod<notPOD>() << '\n';
}
2条答案
按热度按时间7jmck4yq1#
首先,术语“POD类型”已经过时了,
std::is_pod
从C++20开始就被弃用了。该概念已经被分成多个更具体的概念,这些概念更适用于人们期望的POD类型的特定行为。特别是新的概念是 * 平凡类型 * 和 * 标准布局类型 *。你的问题中的问题是这个类是否是“平凡的”。如果一个类是 trivially-copyable 的,并且至少有一个合格的默认构造函数,并且所有这样的构造函数都是 trivial 的,那么它是平凡的。
一个默认的构造函数只有当它,非正式地说,“不做任何事情”,并且它不是“用户提供的”时才是“平凡的”。在你的例子中,默认构造函数在两种情况下都不做任何事情,但是在第一个例子中它不是 * 用户提供的 *,而在第二个例子中它是。如果构造函数不是隐式声明的(即,用户实际上已经写了它的声明),并且它在它的第一声明上不是默认的,即,在类内部,这是你的两个例子之间的区别。
这个规则的原因是所有的翻译单元必须就默认构造函数和类是否是平凡的达成一致。有语言规则附加到这种类型特质,在不同的翻译单元中不能有所不同。
如果构造函数在类中是默认的,那么所有的翻译单元必须对此达成一致,因为具有类定义的翻译单元也必须在其声明中默认构造函数。不同翻译单元中的类定义必须是标记相同的,否则定义将违反一个定义规则,程序将无效。
如果在类外默认构造函数,则只有具有默认构造函数的一个翻译单元会看到它。另一个转换单元可以包括类定义,但不需要包括构造器的默认定义,因此它可能不知道它是默认的。然后,不可能说构造器是否在每个翻译单元中是用户提供的,并且特别地,其他翻译单元可能不知道构造器是否实际上“不做任何事情”。所以不能被认为是微不足道的。
换句话说,trait必须 * 仅 * 依赖于类定义,以便在翻译单元之间保持一致。它不能依赖于类定义后的附加声明。因此,规则是这样的构造函数和类不是平凡的。
puruo6ea2#
**TLDR;
为什么POD被认为是POD,而notPOD被认为不是POD?
因为构造函数
notPOD::notPOD()
在它的第一个声明中不是默认的,这意味着notPOD
有一个用户提供的构造函数,这意味着notPOD
有一个非平凡的默认构造函数,这反过来意味着notPOD
不是一个平凡的类。这进一步意味着notPOD
不是POD类。这在下文中详细描述。从POD类:
POD类是一个POD结构或POD联合的类。
现在,POD结构体被定义为:
POD结构体是一个非联合类,它既是普通类又是标准布局类,并且没有非POD结构体、非POD联合(或此类类型的数组)类型的非静态数据成员。
(强调我的)
但是
notPOD
没有满足平凡类的第二个要求,但POD
满足了。从平凡类:平凡类是具有平凡默认构造函数([class.ctor])并且可以平凡复制的类。
接下来我们来看看平凡的默认构造函数:
如果不是用户提供的,则默认构造函数是平凡的,如果:
最后,它归结为构造函数
notPOd::notPOD()
是否是用户提供的:特殊成员函数如果是用户声明的,并且在第一次声明时没有显式默认或删除,则为用户提供。
(强调我的)
这意味着
POD::POD()
不是用户提供的,所以它是一个POD类,而notPOD::notPOD()
是用户提供的,因为它在第一次声明时没有默认值,所以notPOD
不是POD类。