C++ C++;使具有显式构造函数的类的副本初始化工作?
考虑以下代码:C++ C++;使具有显式构造函数的类的副本初始化工作?,c++,c++14,language-lawyer,c++17,explicit,C++,C++14,Language Lawyer,C++17,Explicit,考虑以下代码: struct X{ explicit X(){} explicit X(const X&){} }; void foo(X a = X()){} int main(){} 使用C++14标准,GCC7.1和Clang4.0都可以编写代码,这正是我所期望的 但是,使用C++17(-std=C++1z),代码。什么规则改变了 对于两个编译器都表现出相同的行为,我怀疑这是一个bug。但据我所知,最新的草案仍然说,default参数使用copy initi
struct X{
explicit X(){}
explicit X(const X&){}
};
void foo(X a = X()){}
int main(){}
使用C++14标准,GCC7.1和Clang4.0都可以编写代码,这正是我所期望的
但是,使用C++17(-std=C++1z
),代码。什么规则改变了
对于两个编译器都表现出相同的行为,我怀疑这是一个bug。但据我所知,最新的草案仍然说,default参数使用copy initialization 1的语义。同样,我们知道
explicit
构造函数只允许直接初始化2
1:;
2:因为C++17的行为发生了变化;在这种情况下,必须删除副本
强制省略复制/移动操作
在以下情况下,编译器需要省略类对象的复制和移动构造,即使复制/移动构造函数和析构函数有明显的副作用。对象被直接构造到存储中,否则它们将被复制/移动到存储中。复制/移动构造函数不需要存在或可访问:
- 在对象初始化过程中,当初始化器表达式为
一个与
变量类型:
T f() { return T(); } T x = T(T(f())); // only one call to default constructor of T, to initialize x
注意:上面的规则没有指定优化:PR值和临时的C++ 17核心语言规范与早期C++修订的根本不同:不再有临时的复制/移动。描述C++17机制的另一种方法是“非物质化的值传递”:返回和使用prvalues,而从不具体化临时值
以及: 复制初始化的效果是:- 首先,如果
是类类型,并且初始值设定项是prvalue 其cv非限定类型与T
的类相同的表达式 初始值设定项表达式本身,而不是临时物化 从中,用于初始化目标对象:请参见(从C++17开始)T
- 如果
是一个类别类型,则cv不合格版本为 另一个是T
或从T
派生的类,它是 检查T
,并通过过载分辨率选择最佳匹配。 然后调用构造函数来初始化对象T
这意味着对于
xa=X()
,将直接默认构造a
,复制/移动构造函数及其副作用将被完全忽略。不会选择用于重载解析的非显式构造函数,这在C++14(及之前版本)中是必需的。对于这些有保证的情况,复制/移动构造函数不参与,那么它们是否显式就无关紧要了。对于问题规则中的示例来说,最重要的是[expr.type.conv]/2。但让我们从以下方面开始:
初始值设定项的语义如下所示。目标类型是要初始化的对象或引用的类型,源类型是初始化器表达式的类型。如果初始值设定项不是单个(可能带括号)表达式,则不定义源类型
-如果目标类型为(可能符合cv条件的)类别类型:
-如果初始值设定项表达式是prvalue,并且源类型的cv非限定版本与目标的类是同一个类,则初始值设定项表达式用于初始化目标对象。[示例:tx=T(T(T());
调用T
默认构造函数来初始化x
。 — [结束示例]
因此,在xa=X()
中,初始值设定项表达式X()
用于初始化目标对象。当然,这还不足以回答:为什么选择默认构造函数(即X()
如何变成()
)以及为什么显式
默认构造函数是好的
X()
表达式是函数表示法中的显式类型转换,因此让我们研究一下:
如果初始值设定项是带括号的单个表达式,则类型转换表达式与相应的强制转换表达式等效(定义为,如果定义为含义)。如果类型为cv void且初始值设定项为(),则表达式是指定类型的prvalue,不执行初始化否则,表达式是指定类型的prvalue,其结果对象由初始值设定项直接初始化
相关句子的重点是我的。它说对于X()
:
- 对象是用
初始化的(它是[expr.type.conv]/1的“初始值设定项”),这就是选择默认构造函数的原因()
- 对象是直接初始化的,这就是为什么默认构造函数是显式的
更多详细信息:当初始值设定项为
()
时,应用:
如果初始值设定项为()
,则对象的值已初始化
:
对于类型为T的对象,表示:-如果
T
是一个(可能是cv限定的)类类型,没有默认构造函数([class.ctor]),或者是用户提供或删除的默认构造函数,则对象默认初始化
:
对于类型为T
的对象,表示:-如果
T
是一个(可能是cv限定的)类类型,则考虑构造函数。枚举了适用的构造函数(),并通过选择初始化器()
的最佳构造函数。调用这样选择的构造函数(参数列表为空)来初始化对象
类类型的对象直接初始化时,从表达式o复制初始化