我有以下代码:
#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行的相同输出。在这种情况下,正在创建的对象在哪里?
谢谢你, 艾哈迈德。
答案 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.