为什么在对象传递给函数时复制未调用的构造函数

时间:2014-10-19 18:04:27

标签: c++ c++11

我有以下代码:

#include <iostream>
using namespace std;

  class foo
  {
      public:
          foo(int a, int b) :
              a_(a), b_(b)
          { cout << "regular const" << endl; }

          foo(const foo& f) :
              a_(f.a_), b_(f.b_)
          { cout << "copy const" << endl; }

          foo& operator=(const foo& rhs)
          {cout << "copy= const" << endl; a_ = rhs.a_; b_ = rhs.b_; return *this; }

          int a_, b_;
  };

  void bar(foo f)
  { }

  int main()
  {
      foo f1(10, 20);
      cout << "------" << endl;

      bar(f1);
      cout << "------" << endl;

      bar(foo(11, 22));            // line 29
      cout << "------" << endl;

      bar({13, 23});               // line 32
      cout << "------" << endl;    

  }

我得到以下输出:

$ ./a.out
regular const
------
copy const
------
regular const
------
regular const
------

对于第29行和第32行,我期望在main中创建一个临时对象(调用常规构造函数),然后在传递给bar()时调用一个复制构造函数。从输出中我看到编译器做了一些优化,猜测可能只是在调用bar()时只在堆栈上创建对象而只调用常规构造函数。有人可以帮助我了解正在进行的优化类型或发生了什么。

使用与生成代码等效的29和32行调用bar()?我理解第29行更具可读性。

我按如下方式更改了bar():

 void bar(const foo& f)
 { }

我得到第29行和第32行的相同输出。在这种情况下,正在创建的对象在哪里?

谢谢你, 艾哈迈德。

1 个答案:

答案 0 :(得分:3)

第32行,

bar({13, 23});

参数是根据 copy-list-initialization 初始化的 - 没有创建中间临时,甚至理论上也没有。

bar(foo(11, 22));

这里涉及复制省略。允许编译器忽略临时,即将对象直接构造到参数:

  

复制/移动操作的省略,称为 copy elision ,是   在下列情况下允许(可以合并到   消除多份副本):
[...]
- 当一个临时类对象时   未被绑定的引用(12.2)将被复制/移动到   类对象具有相同的cv-unqualified类型,复制/移动   通过直接构造临时对象可以省略操作   进入省略的复制/移动目标

如果要查看输出而没有任何省略的副本或移动,请在GCC中使用-fno-elide-constructors。它将产生您期望的输出。至少对于第32行。

现在到第二个版本吧:

void bar(const foo& f)

在第29行,从braced-init-list创建并初始化临时。在第32行中,引用绑定到临时参数。不涉及任何优化或合作。

bar({13, 23});    // A temporary object is created as if by foo{13, 23},
                  // and the reference is bound to it

bar(foo(11, 22)); // The reference is bound to the temporary.
                  // The lifetime of the temporary has been extended.