基于std::is_constructible示例的Foo类构造特性疑问
示例代码及运行结果
#include <iostream> #include <type_traits> class Foo { int v1; double v2; public: Foo(int n) : v1(n), v2() {} Foo(int n, double f) noexcept : v1(n), v2(f) {} }; int main() { auto is = [](bool o) { return (o ? "\t" "is " : "\t" "isn't "); }; std::cout << "Foo ...\n" << is(std::is_trivially_constructible_v<Foo, const Foo&>) << "Trivially-constructible from const Foo&\n" << is(std::is_trivially_constructible_v<Foo, int>) << "Trivially-constructible from int\n" << is(std::is_constructible_v<Foo, int>) << "Constructible from int\n" << is(std::is_nothrow_constructible_v<Foo, int>) << "Nothrow-constructible from int\n" << is(std::is_nothrow_constructible_v<Foo, int, double>) << "Nothrow-constructible from int and double\n"; }
运行输出:
Foo ...
is Trivially-constructible from const Foo&
isn't Trivially-constructible from int
is Constructible from int
isn't Nothrow-constructible from int
is Nothrow-constructible from int and double
问题解答
i) Foo可从const Foo&(拷贝构造)平凡构造的原因
没错,就是因为编译器自动生成的拷贝构造函数属于平凡构造函数。C++标准规定:当类没有显式定义拷贝构造函数,且所有非静态数据成员的拷贝构造都是平凡的,同时基类(如果存在)的拷贝构造也平凡时,编译器生成的拷贝构造函数就是平凡的。这里Foo的两个成员int v1和double v2都是基本类型,它们的拷贝构造本身就满足平凡性要求,所以编译器自动生成的拷贝构造函数符合平凡构造的判定标准,因此std::is_trivially_constructible_v<Foo, const Foo&>返回true。
ii) Foo(int)构造函数无法让Foo从int平凡构造的原因
平凡构造函数的核心要求之一是:必须是编译器自动生成的,而非用户自定义的构造函数。这里的Foo(int)是用户显式编写的构造函数,哪怕它的逻辑很简单,也直接失去了平凡构造的资格。另外,构造函数里的v2()属于值初始化操作,会主动将double成员设为0.0,这种显式指定的初始化行为,也会让构造函数不符合平凡构造的要求——平凡构造函数不允许有用户指定的初始化逻辑,只能依赖默认的逐位拷贝或基本类型的默认初始化。综上,这个用户自定义构造函数不满足平凡构造的条件,所以std::is_trivially_constructible_v<Foo, int>返回false。
内容的提问来源于stack exchange,提问作者Vinod

