允许这样做的设计原理是什么
const Foo& a = function_returning_Foo_by_value();
但不是这个
Foo& a = function_returning_Foo_by_value();
?
第二行可能出现什么问题(第一行不会出现问题)?
我会回答你的问题……反过来。
为什么他们允许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
? 引发异常?而且由于它只是一个运行时解决方案,显然不能令人满意。
将临时对象绑定到引用的想法很脆弱。
非常量指针不会延长临时对象的生命周期的原因是非常量引用首先不能绑定到临时对象。
这有很多原因,我将只展示一个涉及隐式扩展转换的经典示例:
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]);
请注意,在这种情况下不需要延长寿命。
我理解的基本原理如下:当一个临时对象超出范围时,它会被销毁。如果你保证不修改它,我会让你延长它的寿命。
“可能出错的地方”是您修改了一个对象,然后立即丢失了更改,因此定义了规则以帮助您不犯此类错误。您可能会认为,如果再次调用该函数,您将获得一个包含更改的对象,当然您不会这样做,因为您修改了副本。
创建临时然后在其上调用非常量方法的典型情况是当您要交换它时:
std::string val;
some_func_that_returns_a_string().swap( val );
这有时非常有用。