提问人:BostonLogan 提问时间:11/19/2009 更新时间:3/30/2012 访问量:3765
为什么在这种情况下不调用复制构造函数?
Why copy constructor is not called in this case?
问:
下面是小代码片段:
class A
{
public:
A(int value) : value_(value)
{
cout <<"Regular constructor" <<endl;
}
A(const A& other) : value_(other.value_)
{
cout <<"Copy constructor" <<endl;
}
private:
int value_;
};
int main()
{
A a = A(5);
}
我假设输出将是“常规构造函数”(对于 RHS),然后是 LHS 的“复制构造函数”。所以我避免了这种风格,并始终将类的变量声明为 .但令我惊讶的是,在上面的代码中,复制构造函数从未被调用(Visual C++ 2008)A a(5);
有谁知道这种行为是编译器优化的结果,还是C++的一些记录(和可移植)功能?谢谢。
答:
来自另一条评论:“所以默认情况下我不应该依赖它(因为它可能取决于编译器)”
不,它几乎不依赖于编译器。任何有价值的编译器都不会浪费时间构建一个 A,然后复制它。
在标准中,它明确指出,它完全可以接受,等同于说。(§12.8.15,第211页)这样做显然是多余的,所以它去掉了内部的.T = x;
T(x);
T(T(x))
T
若要获得所需的行为,需要强制编译器默认构造第一个 A:
A a;
// A is now a fully constructed object,
// so it can't call constructors again:
a = A(5);
评论
在这里,您有 from temporary 的复制初始化。根据 C++ 标准 12.2/2,允许在此处跳过调用复制构造函数的实现。a
A(5)
评论
A a = A(5);
这条线相当于
A a(5);
尽管它的外观是函数式的,但第一行只是用参数 5 构造的。不涉及复制或临时。根据 C++ 标准,第 12.1.11 节:a
函数表示法类型转换 (5.2.3) 可用于创建其类型的新对象。[ 注: 语法看起来像构造函数的显式调用。——尾注 ]
评论
我正在研究这个问题,以回答另一个被当作骗子而关闭的问题,所以为了不让工作白费,我回答了这个问题。
该形式的语句称为变量的复制初始化。C++11 标准 8.5/16 规定:A a = A(5)
a
所选函数的初始值设定项表达式为 它的论点;如果函数是构造函数,则调用将初始化 目标类型的 CV 不合格版本的临时版本。这 temporary 是一个 prvalue。调用的结果(即临时的 对于构造函数情况),然后用于直接初始化,根据 根据上述规则,作为目标的对象 复制初始化。在某些情况下,允许实施 通过以下方式消除此直接初始化中固有的复制 将中间结果直接构造到对象中 初始 化;见12.2、12.8。
这意味着编译器查找要处理的适当构造函数,创建一个临时构造函数,并将该临时构造函数复制到 中。但是在什么情况下可以消除副本呢?A(5)
a
让我们看看 12.8/31 是怎么说的:
当满足某些条件时,允许实现省略 类对象的复制/移动构造,即使复制/移动 对象的构造函数和/或析构函数具有副作用。在 在这种情况下,实现将 省略了复制/移动操作,只是两种不同的引用方式 到同一个对象,并且该对象的破坏发生在 后来这两个物体会被摧毁 没有优化。这种复制/移动操作的省略, 在以下情况下允许使用“复制省略”(copy elision) (可以合并以消除多个副本):
[...]
- 复制/移动未绑定到引用的临时类对象 (12.2) 时 对于具有相同 CV-Unqualified 类型的类对象,复制/移动操作可以是 通过将临时对象直接构造到省略的复制/移动的目标中来省略
考虑到所有这些,以下是表达式会发生什么:A a = A(5)
- 编译器看到带有复制初始化的声明
- 选择构造函数以初始化临时对象
A(int)
- 由于临时对象未绑定到引用,并且它与复制初始化表达式中的目标类型相同,因此允许编译器直接将对象构造为 ,省略临时
A
a
评论