最近的一个问题让我想知道显式的拷贝构造函数。这是我尝试在Visual Studio 2005下编译的示例代码:
struct A
{
A() {}
explicit A(const A &) {}
};
// #1 > Compilation error (expected behavior)
A retByValue()
{
return A();
}
// #2 > Compiles just fine, but why ?
void passByValue(A a)
{
}
int main()
{
A a;
A b(a); // #3 > explicit copy construction : OK (expected behavior)
A c = a; // #4 > implicit copy construction : KO (expected behavior)
// Added after multiple comments : not an error according to VS 2005.
passByValue(a);
return 0;
}
现在提出问题:
[编辑] 我刚刚在MSDN找到了一个有趣的链接,情况完全相同,主要功能是一个神秘的评论:“c被复制”(好像是明显)。正如Oli Charlesworth所指出的那样:gcc没有编译这段代码,我相信他是对的。
答案 0 :(得分:41)
我相信C ++ 03的相关部分是§12.3.1 2:
显式构造函数与非显式构造函数一样构造对象,但仅在直接初始化语法(8.5)或强制转换(5.2.9,5.4)的位置明确用过的。默认构造函数可以是显式构造函数;这样的构造函数将用于执行默认初始化或值初始化(8.5)。
和§ 8.5 12:
在参数传递,函数返回,抛出异常(15.1),处理异常(15.3)和大括号括起的初始化列表(8.5.1)中发生的初始化是称为复制初始化,相当于表单
T x = a;
新表达式(5.3.4),static_cast表达式(5.2.9),函数表示法类型转换(5.2.3)以及基本和成员初始值设定项({{3} })被称为直接初始化,相当于形式
T x(a);
根据C ++03§12.3.12,调用passByValue(a)
涉及复制初始化,而不是直接初始化,因此应该是错误。
答案 1 :(得分:5)
passByValue
的定义没问题,因为没有复制A对象的语句。在retByValue
的定义中,当然有一个复制A对象的return语句。
答案 2 :(得分:3)
在C ++ 11之前,使复制构造函数显式化的实际用途是,它实际上是使类不可复制的一部分。
危险在于,虽然您声明复制构造函数是私有的并且没有实现它,但如果您不小心在朋友或类本身中复制了一个,编译器就不会捡起它并且您只会收到难以找到的链接错误。
明确这样做也可以减少这种可能性,因为编译器可能会很好地拾取你的无意副本并指向你正在执行它的实际行。
在C ++ 11(和14)中,使用=delete
语法时不需要执行此操作,因为即使在类本身内或朋友内复制也会出现编译器错误。
答案 3 :(得分:2)
要扩展@MSalters的答案(这是正确的),如果要将passByValue(a);
添加到main()
函数,编译器 应该抱怨它。
显式复制构造函数用于防止这种情况,即防止在函数调用中隐式复制资源等(实质上它强制用户通过引用传递而不是按值传递)。