拥有一个公共拷贝构造函数将构成一个小程序 编译,但没有显示副作用“复制”。
#include <iostream>
class X
{
public:
X(int) { std::cout << "Construct" << std::endl; }
// Having a public copy constructor will make the little program
// compile, but not showing the side effect "Copy".
private:
X(const X&) { std::cout << "Copy" << std::endl; }
private:
X& operator = (const X&);
};
int main() {
X x = 1;
return 0;
}
答案 0 :(得分:8)
您使用了所谓的“复制初始化”(在[decl.init]
中定义)。定义的含义是使用X
构造函数构造int
类型的临时对象,然后使用复制构造函数从临时对象初始化x
。
但是,在这种情况下,该标准还允许称为“复制构造函数省略”(在[class.copy]
中定义)的优化。如果应用该优化,则不存在临时性。 x
是使用int
构造函数构造的,就像您编写了所谓的“直接初始化”X x(1);
一样。
为了不会意外地编写在应用优化时编译的代码而不是在编译时不编写代码,标准要求复制构造函数必须是可访问的,即使它被省略。因此,构造函数必须是公共的,即使(使用您正在使用的编译器和选项)它也不会被调用。
在C ++ 11中,移动构造函数被考虑,并且也有资格获得elision。但是这个类X
没有移动构造函数,所以C ++ 11和C ++ 03对于这个例子是相同的。
答案 1 :(得分:4)
以下是涉及的C ++标准的相关部分:
[dcl.init] / 16,bullet 6,sub-bullet 1:如果初始化是直接初始化,或者它是复制初始化,其中源类型的cv-nonqualified版本是同一类,或者,目标类的派生类,构造函数被认为是.... 如果没有构造函数适用,或者重载决策是不明确的,则初始化是不正确的。 [强调添加] < / p>
换句话说,如果编译器优化可以忽略副本并不重要,则初始化是错误的,因为没有适用的构造函数。当然,一旦您将复制构造器公开,以下部分适用:
[class.copy] / 31:当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数具有副作用....在下列情况下允许复制/移动操作(称为复制省略)的这种省略(可以合并以消除多个副本):
项目符号3:当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-unqualified类型的类对象时,可以通过构造它来省略复制/移动操作临时对象直接进入省略的复制/移动目标
答案 2 :(得分:2)
我最好的猜测是这是一个编译器优化。如果您有复制构造函数,则有一种以这种方式声明对象的有效路径。考虑明确地这样做:
int main() {
X x(X(1));
return 0;
}
或更明确地说:
int main() {
X intermediate(1);
X x(intermediate);
return 0;
}
因此,它不是使用operator=
,而是知道您在声明后尝试初始化对象。从本质上讲,编译器是“聪明的”。最后,它再次对此进行了优化:
int main() {
X x(1);
return 0;
}
因此,在编译器计算出“你想要做什么”之后,这将成为对象的标准初始化。
修改强>
为完整起见,请注意,如果您尝试这样做:
int main() {
X x = 1;
x = 2;
return 0;
}
您将看到私有operator=
的问题。显而易见,重要的是要注意operator=
从未在上面的原始初始化问题中实际使用,即使代码中出现=
。
答案 3 :(得分:2)
编译器将初始化优化为:
X x(1)
这是一种复制省略,虽然它可以消除您所看到的副作用,但标准允许这样做。
从C ++ 03标准第12.8节:
当满足某些条件时,允许省略实现 复制构造一个类对象,即使是复制构造函数 和/或对象的析构函数有副作用。在这种情况下, 实现处理省略副本的源和目标 操作只是两种不同的引用方式 对象,并且该对象的破坏发生在后期 两个物体在没有物体的情况下被摧毁的时间 优化。 111)允许复制操作的省略 以下情况(可以合并以消除多个 份): - 在具有类返回类型的函数中的areturnstatement中, 当表达式是非易失性自动对象的名称时 与函数返回类型相同的cv-unqualified类型, 通过构造自动对象可以省略复制操作 直接进入函数的返回值 - 临时班级时>尚未绑定到引用的对象(12.2) 将被复制到具有相同cv-nonqualified类型的类对象,即副本 通过构造临时对象可以省略操作 直接进入省略副本的目标
第二种情况就是我们在这里。