允许这个
背后的设计理由是什么?const Foo& a = function_returning_Foo_by_value();
但不是这个
Foo& a = function_returning_Foo_by_value();
第二行可能出现什么问题(第一行不会出错)?
答案 0 :(得分:4)
我会回答你的问题......反过来说。
为什么他们允许Foo const& foo = fooByValue();
开始?
它使生活(有些)更容易,但在整个地方引入了潜在的未定义行为。
Foo const& fooByReference()
{
return fooByValue(); // error: returning a reference to a temporary
}
这显然是错误的,实际上编译器会尽职尽责地报告它。 根据Tomalak的评论:标准没有强制要求,但是好的编译器应该报告它。 Clang,gcc和MSVC呢。我认为Comeau和icc也是如此。
Foo const& fooByIndirectReference()
{
Foo const& foo = fooByValue(); // OK, you're allowed to bind a temporary
return foo; // Generally accepted
}
这是错误的,但更微妙。问题是临时的生命周期绑定到foo
的生命周期,该生命周期超出了函数末尾的范围。 foo
的副本传递给调用者,此副本指向以太。
我提出了Clang的错误,而Argyris能够诊断出这个案例(真的很值得:p。)。
Foo const& fooForwarder(Foo const&); // out of line implementation which forwards
// the argument
Foo const& fooByVeryIndirectReference()
{
return fooForwarder(fooByValue());
}
由fooByValue
创建的临时值绑定到fooForwarder
参数的生命周期,该参数尽职地提供(引用的)副本,即返回给调用者的副本,即使它现在指向以太。
这里的问题是fooForwarder
的实现完全符合标准,但它在调用者中创建了未定义的行为。
令人生畏的事实是,诊断这一点需要了解fooForwarder
的实现,这对编译器来说是遥不可及的。
我能理解的唯一解决方案(除了WPA)是一个运行时解决方案:只要临时绑定到引用,那么您需要确保返回的引用不共享相同的地址...然后是什么? assert
?提出异常?由于它只是一个运行时解决方案,显然不能令人满意。
将临时绑定到引用的想法很脆弱。
答案 1 :(得分:3)
非常量指针不会延长临时值的生命周期的原因是非const引用不能首先绑定到临时值。
有很多原因,我只会展示一个涉及隐式扩展转换的经典示例:
struct Foo {};
bool CreateFoo( Foo*& result ) { result = new Foo(); return true; }
struct SpecialFoo : Foo {};
SpecialFoo* p;
if (CreateFoo(p)) { /* DUDE, WHERE'S MY OBJECT! */ }
允许const引用绑定临时对象的基本原理是它能够实现完全合理的代码:
bool validate_the_cat(const string&);
string thing[3];
validate_the_cat(thing[1] + thing[2]);
请注意,在这种情况下不需要终身扩展。
答案 2 :(得分:3)
我理解的理由如下:当一个临时超出范围时,预计会被销毁。 如果你保证不会修改它,我会让你延长它的寿命。
答案 3 :(得分:0)
“可能出错的地方”是您修改对象然后立即丢失更改,因此定义规则以帮助您不犯这样的错误。您可能会认为如果再次调用该函数,您将获得一个包含更改的对象,当然您不会因为修改了副本而对其进行了修改。
你要创建一个临时的,然后调用非const方法的典型情况是你打算交换它:
std::string val;
some_func_that_returns_a_string().swap( val );
这有时非常有用。