按值返回总是恒量吗?

Is return by value always const?

本文关键字:返回      更新时间:2023-10-16

此代码无法编译:

class C {};
void foo (C& c) {}
C bar() { return C(); }
int main()              
{
foo(bar());
}               

foo(bar())
行中的编译错误 (GCC 4.1.2):

类型为"C&"的非常量引用的初始化无效 来自"C"型临时

由于bar()返回一个mutable对象,它应该编译...
为什么C++不允许上面的代码?


编辑:我在下面的答案中总结了所有答案中的所有好主意;-)

此处适用的规则是不能创建对临时对象的非常量引用。如果foo被声明为foo(const C&)代码就可以了。

但是,临时对象本身不是 const ;您可以在其上调用非 const 成员函数,例如bar().non_const_member_function()

使用 C++11,foo 可以编写为采用右值引用;在这种情况下,调用就可以了:

void foo(C&&);
foo(bar());  // okay

这是因为bar返回的值是一个临时值。由于它的存在是暂时的,因此不能使用指针或引用。

但是,如果您存储该临时对象的副本(如在第二次更改中一样),则不再传递对临时对象的引用foo,而是传递对真实有形对象的引用。在第一种情况下,当您更改为对常量对象的引用时,编译器会确保临时对象保留足够长的时间(根据C++规范)。

问题不在于bar的声明,而在于foo的声明。foo采用非const引用,临时引用只能绑定到const引用(这会延长临时引用的生存期以匹配其绑定到的引用的生存期)。

允许非const引用绑定到临时引用没有多大意义。非const引用意味着它将修改绑定到它的任何对象。修改临时没有任何用处,因为它的生存期是有限的,一旦超出范围,更改就会丢失。

可修改(左值)引用不绑定到临时值。但是,常量引用确实绑定到临时值。它与值返回的对象是否为 const 无关;这只是表达式是否是临时的问题。

例如,以下内容有效

struct C { void i_am_non_const() {} };
int main()
{
bar().i_am_non_const();
}

这是一个设计选择。这里没有什么本质上是不可能的。只是一个设计选择。

在 C++11 中,您有第三种选择,这也是更好的选择:

void foo(C && c) {} 

也就是说,使用右值引用。

它不是常量,但它是一个临时的右值。因此,它不能绑定到非常量左值引用。

它可以绑定到常量或右值引用,并且可以在其上调用成员函数(常量或非常量):

class C { void f(); };
void foo_const(C const &);
void foo_rvalue(C &&);
foo_const( bar() );  // OK
foo_rvalue( bar() ); // OK
bar().f();           // OK

真正的硬事实是,获取对临时值的引用是没有意义的。

通过引用传递对象的要点是它允许您修改其状态。但是,在临时的情况下,就其本质而言,能够修改它并不是特别有用,因为您无法稍后在代码中获取对它的另一个引用以查看更改。

但是,如果您具有const引用,则有所不同。由于您只会从const参考中阅读,因此能够在那里使用临时是完全有意义的。这就是为什么编译器会为你"破解"它,并为你想要"变成"const引用的临时人员提供一个更永久的地址。

因此,规则是不能获取对临时值的非const引用。(这在 C++11 中略有变化,我们有一种新型的引用可以达到这个确切的目的,但方法应该以特殊的方式处理这些引用。

谢谢大家的回答:-)
在这里我收集了你们的好主意;-)

值返回不是const。例如,我们可以调用按值返回的非常量成员函数:

class C { 
public:
int x;
void set (int n) { x = n; }  // non-const function
};
C bar()  { return C(); }
int main ()
{
bar.set(5); // OK
}

C++不允许对临时对象的非常量引用
但是C++11允许对临时对象的非常量右值引用

解释

class C {};
void foo (C& c) {}
C bar()  { return C(); }
//bar() returns a temporary object
//temporary objects cannot be non-const referenced
int main()
{
//foo() wants a mutable reference (i.e. non-const)
foo( bar() ); // => compilation error     
}                 

三个修复

  1. 更改foo声明

    void foo (const C& c) {}
    
  2. 使用其他对象

    int main()              
    {
    C c;
    foo( c = bar() );
    }
    
  3. 使用 C++11右值引用

    void foo(C && c) {}
    

此外

为了确认临时对象是 const 的,上述源代码出于同样的原因失败:

class C {};
void foo(C& c) {}
int main()                
{
foo( C() );
}