按值返回总是恒量吗?
Is return by value always const?
此代码无法编译:
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
}
三个修复
更改
foo
声明void foo (const C& c) {}
使用其他对象
int main() { C c; foo( c = bar() ); }
使用 C++11右值引用
void foo(C && c) {}
此外
为了确认临时对象是 const 的,上述源代码出于同样的原因失败:
class C {};
void foo(C& c) {}
int main()
{
foo( C() );
}
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- 什么时候在C++中返回常量引用是个好主意
- 你能重载对象变量名本身返回的内容吗
- 为什么 Serial.println(<char[]>);返回随机字符?
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 如何获取std::result_of函数的返回类型
- QueryWorkingSet总是返回false
- (C++)分析树以计算返回错误值的简单算术表达式
- 访问者访问变体并返回不同类型时出错
- 如何返回一个类的两个对象相加的结果
- OpenInventor从9.8升级到10.4.2后,GLSL纹理返回零
- lower_bound()返回最后一个元素
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- 奇怪的(对我来说)返回声明 - 在谷歌上找不到任何关于它的信息
- 如何取消对nullptr的屏蔽,返回正确的对象
- 奇怪的结构&GCC&clang(void*返回类型)
- 架构决策:返回std::future还是提供回调
- 从python中调用C++函数并获取返回值
- 矩阵向量乘法(cublasDgemv)返回零
- 为什么模板类中的对象不能返回值