class x
{
int a;
public:
x()
{
cout<<"\n\ndefault constructor";
}
x(x& obj)
{
cout<<"\n\ncopy constructor";
}
x fun()
{
x ob;
return ob;
}
};
int main()
{
x ob1;
x ob2=ob1.fun();
return 0;
}
最初,这段代码给出了一个错误“没有匹配函数来调用'x :: x(x)'”, 当我将复制构造函数更改为
时x(const x& obj)
{
cout<<"\n\ncopy constructor";
}
输出变为
默认构造函数
默认构造函数
还是复制构造函数没有执行....为什么?
答案 0 :(得分:14)
这称为由编译器完成的copy-elision,语言规范允许这样做。
请参阅此wiki条目:
至于为什么非const版本给出编译错误,因为obj1.fun()
返回一个临时对象,它不能绑定到非const引用,但是它可以绑定到const引用,所以const版本编译得很好。一旦你使它成为const引用,它只用于语义检查,但编译器优化了代码,忽略了对copy-constructor的调用。
但是,如果使用GCC的-fno-elide-constructors
选项编译它,则不会执行copy-elision,并且将调用copy-constructor。 GCC doc说,
<强> -fno-的Elid-构造强>
C ++标准允许实现省略创建临时文件,该临时文件仅用于初始化相同类型的另一个对象。 指定此选项会禁用该优化,并强制G ++在所有情况下都调用复制构造函数。
答案 1 :(得分:3)
编译器决定优化复制构造,因为它允许这样做,即使它通过const引用接受了参数。
答案 2 :(得分:1)
临时值是rvalues,不绑定到非常量引用。因此ob1.fun()
无法绑定到x::x(x&)
构造函数。
但是,rvalues 绑定到常量引用。
至于为什么没有输出:复制构造函数是标准中的一个特例,并且允许编译器忽略复制构造函数调用,即使复制构造函数有副作用。但是,施工必须仍然有效!另一个这样的例子是,如果你声明了复制构造函数explicit
- 它仍然会被删除,但你的代码将是错误的。
在GCC中,你可以说-fno-elide-constructors
带回你的构造函数。
答案 3 :(得分:1)
第一个错误是因为它试图将复制构造与临时对象一起使用。
ob1.fun(); // This returns an x by value
因此当你使用它时听到
x ob2=ob1.fun(); // You are passing it by value which requires a local temporary
// This is equivalent to this:
x ob2(obj1.fun()); // So it is trying to do a copy construction.
Temporaries只能绑定到const引用。因此无法编译。
修复该问题后(使用临时问题)现在可以使用复制构造函数。但是如果可以的话,允许编译器优化它。它无法优化它,因为它甚至不允许在第一个版本中使用它。
答案 4 :(得分:1)
由于复制省略优化,您的副本构造函数调用被跳过。您不能依赖复制构造函数(如print语句)中的任何可观察行为。如果您确实希望看到它的输出,请尝试禁用优化。