拥有一个公共拷贝构造函数会使小程序编译,但不会显示副作用"复制".
#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;
}
Run Code Online (Sandbox Code Playgroud)
您使用了所谓的"复制初始化"(定义[decl.init]).定义的含义是X使用int构造函数构造类型的临时对象,然后x使用复制构造函数从临时对象初始化.
但是,该标准还允许[class.copy]在这种情况下称为"复制构造函数省略"(在其中定义)的优化.如果应用该优化,则不存在临时性.x使用int构造函数构造,就像您编写所谓的"直接初始化"一样X x(1);.
为了使您不会意外地编写在应用优化时编译的代码而不是在不应用优化时编写的代码,标准要求复制构造函数即使被省略也必须是可访问的.因此,构造函数必须是公共的,即使(使用您正在使用的编译器和选项)它也不会被调用.
在C++ 11中,移动构造函数被考虑,并且也有资格获得elision.但是这个类X没有移动构造函数,所以C++ 11和C++ 03对于这个例子是相同的.
以下是涉及的 C++ 标准的相关部分:
[dcl.init]/16,项目符号 6,子项目符号 1:如果初始化是直接初始化,或者如果是复制初始化,其中源类型的 cv 未限定版本与相同的类或派生类类、目标类、构造函数被考虑...如果没有构造函数适用,或者重载决策不明确,则初始化格式错误。[强调]
换句话说,编译器优化是否可以消除副本并不重要,初始化的格式不正确,因为没有适用的构造函数。当然,一旦您将复制构造函数公开,以下部分将适用:
[class.copy]/31:当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数有副作用...... . 这种复制/移动操作的省略称为复制省略,在以下情况下是允许的(可以组合起来消除多个副本):
项目符号 3:当未绑定到引用 (12.2) 的临时类对象被复制/移动到具有相同 cv-unqualified 类型的类对象时,可以通过直接构造临时对象来省略复制/移动操作进入省略的复制/移动的目标