我有一个返回Foo
类型的对象的函数:
Foo getFoo();
我知道以下内容会编译并且会有效,但为什么我会这样做?
const Foo& myFoo = getFoo();
对我来说,以下内容更具可读性,并且不会强迫我记住C ++允许我为const引用分配r值:
const Foo myFoo = getFoo();
两者有什么区别?为什么我会在第二次使用第一个?为什么我会在第一个使用第二个?
答案 0 :(得分:4)
我认为GotW #88会回答这个问题
答案 1 :(得分:4)
与流行的观点相反,无法保证将按值返回对象的函数的结果分配给const引用将导致比将对象分配给对象本身更少的副本。
将rvalue分配给const引用时,编译器可以通过两种方式之一绑定引用。它可以通过复制rvalue并将引用绑定到该临时值来创建一个新的临时值,或者它可以将引用直接绑定到rvalue本身。
如果编译器无法进行“显而易见”的优化以删除临时值而忽略复制构造函数的返回值getFoo
,那么它有多大可能能够执行更有效的表单将rvalue绑定到const引用而不创建新的临时值?
使用const引用的一个原因是使函数对潜在的切片更加健壮。如果返回类型实际上是从Foo
派生的类型,那么即使编译器确实从函数返回的rvalue中创建了一个临时对象,也可以保证不会切片分配给基类const引用。无论基类中的析构函数是否为虚拟,编译器还将生成对派生类析构函数的正确调用。这是因为创建的临时对象的类型基于所分配的表达式的类型,而不是基于正在初始化的引用的类型。
请注意,返回值的副本数量问题完全独立于返回值优化和命名的返回值优化。这些优化指的是消除将返回表达式或命名局部变量的rvalue结果的副本复制到函数体内函数的返回值中。显然,在最好的情况下,可以进行返回值优化,并且可以消除返回值的临时值,从而不会对返回的对象执行复制。
答案 2 :(得分:1)
允许这种模式是有效的:
void foo(const SomeType &);
foo(SomeType(bar))
在这种情况下,构造一个临时SomeType并传递给foo。最有可能的事实是,您还可以在堆栈上对临时工具进行常量引用,这是用于在标准中定义此行为的措辞的副作用。请注意,正如评论中提到的那样,临时的生命周期被扩展为引用本身的生命周期。
答案 3 :(得分:0)
可能有以下几个原因:
如果你不想引用const对象怎么办? 例如,这不起作用:
const Foo &myFoo = getFoo(); myFoo.myfield = x;
或者,如果从getFoo()返回临时对象怎么办?这将警告将引用(或地址)返回到本地:
const Foo &getFoo(void) { Foo localFoo(); // do the things you want to localFoo return( localFoo ); }
const Foo& myFoo = getFoo()
的内部完全相同
Foo myFoo = getFoo()
所以const ref返回的性能值的参数是无效的。我发现返回合理大小的对象没问题。
免责声明 - 我没有在gcc上尝试这些例子。您的里程可能会有所不同。