以下是小代码段:
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的“复制构造函数”。所以我避免使用这种样式,并始终将类的变量声明为A a(5);
。但令我惊讶的是,上面的代码中永远不会调用复制构造函数(Visual C ++ 2008)
有人知道这种行为是编译器优化的结果,还是C ++的一些记录(和可移植)功能?感谢。
答案 0 :(得分:14)
从另一条评论:“所以默认情况下我不应该依赖它(因为它可能取决于编译器)”
不,它实际上并不依赖于编译器。任何值得一粒沙子的编译器都不会浪费时间构建A,然后将其复制过来。
在标准中明确指出,T = x;
完全可以接受T(x);
。 (§12.8.15,第211页)使用T(T(x))
执行此操作显然是多余的,因此它会移除内部T
。
要获得所需的行为,您需要强制编译器默认构造第一个A:
A a;
// A is now a fully constructed object,
// so it can't call constructors again:
a = A(5);
答案 1 :(得分:7)
我正在研究这个问题以回答另一个被作为欺骗而被关闭的问题,所以为了不让工作浪费,我正在回答这个问题。
A a = A(5)
形式的声明称为变量a
的复制初始化。 C ++ 11标准,8.5 / 16表示:
使用初始化表达式调用所选函数 它的论点;如果函数是构造函数,则调用初始化a 临时的目标类型的cv不合格版本。该 临时是一个prvalue。通话的结果(这是暂时的 然后根据构造函数用于直接初始化 对于上面的规则,作为目标的对象 副本初始化。 在某些情况下,允许实施 消除这种直接初始化中固有的复制 将中间结果直接构造到对象中 初始化;见12.2,12.8 。
这意味着编译器会查找相应的构造函数来处理A(5)
,创建一个临时文件并将其临时复制到a
。但在什么情况下可以删除副本?
让我们看看12.8 / 31的内容:
当满足某些条件时,允许省略实现 复制/移动类对象的构造,即使复制/移动 对象的构造函数和/或析构函数有副作用。 在这种情况下,实施处理的来源和目标 省略了复制/移动操作,只是两种不同的引用方式 对于同一个对象,该对象的破坏发生在 稍后两个物体被摧毁的时间 没有优化。这种复制/移动操作的省略, 在下列情况下允许使用 copy elision (可以合并以消除多个副本):
[...]
- 当复制/移动尚未绑定到引用(12.2)的临时类对象时 对于具有相同cv-unqualified类型的类对象,可以进行复制/移动操作 通过将临时对象直接构造到省略的copy / move
的目标中而省略
考虑到所有这些,这就是表达式A a = A(5)
:
A(int)
构造函数来初始化临时对象A
,所以允许编译器直接将对象构造成a
,省略临时答案 2 :(得分:4)
您可以从临时a
获得A(5)
的复制初始化。根据C ++标准12.2 / 2,允许实现跳过调用复制构造函数。
答案 3 :(得分:-1)
A a = A(5);
这一行相当于
A a(5);
尽管它具有函数式外观,但第一行只是用参数5构造a
。不涉及复制或临时。从C ++标准,第12.1.11节:
功能表示法类型转换(5.2.3)可用于创建其类型的新对象。 [注意: 语法看起来像构造函数的显式调用。 - 后注]