2

从下面的代码中,我期望 CA 类会调用以下代码

  1. 构造函数创建要由函数返回的临时对象foo
  2. a复制构造函数创建要传递给主变量的变量
  3. 另一个复制构造函数,它将a根据函数返回的值创建变量。

为什么不是这样?我得到的结果只是

A

在我期待的时候

ABB

所以只有构造函数被调用。编译器是在幕后优化某些东西还是我错过了一些 C++ 概念?

class CA{
public:
   CA(){ std::cout << "A"; }
   CA( const CA& ){ std::cout << "B"; }
   CA& operator=(const CA& ){ std::cout << "C";return *this; }
};

CA foo(){
      return CA();
}

int main(){
 CA a = foo();    
}
4

2 回答 2

8

编译器是在幕后优化某些东西还是我错过了一些 C++ 概念?

发现!这称为复制省略。在谷歌上查找 RVO 和 NRVO。您还应该查找三规则。

复制省略是编译器允许执行的唯一影响可观察行为的优化。出于这个原因,您不应该在复制构造函数中放置重要的逻辑。

于 2012-07-14T16:45:59.703 回答
0

C++ 中至少有两种“类型”的优化。

  • 第一类是语言规范明确引入的特定优化。

  • 第二种是编译器在“as-if”规则下所做的所有那些疯狂和不可预测的优化(即,只要程序的可观察行为保持不变,编译器就可以做任何事情)。

(有人可能会说只有第二种优化才是真正的优化。)

您在这里看到的是第一种优化。在执行多步复制操作时,语言规范总是明确允许编译器消除中间临时副本。

此外,C++03 和更高版本的语言规范更进一步:它们明确允许编译器执行“命名返回值优化”(NRVO),这实际上消除了命名(非临时)对象。

两者都减少了程序中的复制操作次数。

C++ 中的这种复制消除优化是允许的,即使它们改变了程序的可观察行为,即第一种优化有时会违反对第二种的限制。在您的情况下,即使您将 I/O 操作插入到复制构造函数中,仍然允许编译器消除对该构造函数的调用。

您发布的代码不需要 NRVO。一个好的 C++98 编译器应该能够产生你观察到的结果。如果您想查看您的编译器在这种情况下是否执行 NRVO,您可能想试试这个

CA foo(){
  Ca ca;
  return ca;
}
于 2012-07-14T16:52:46.060 回答