允许这样做的设计原理是什么
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 );
这有时非常有用。