2017-05-06 41 views
2

为什么下面的代码...引用匿名右值已损坏

#include <iostream> 
#include <map> 

template< typename T, typename U > 
class Map 
{ 
public: 
    Map(const T& t, const U& u) { map_[ t ] = u; } 
    Map< T, U >& operator() (const T& t, const U& u) 
    { 
    map_[ t ] = u; 
    return *this; 
    } 
    U& operator[] (const T& t) { return map_[ t ]; } 

private: 
    std::map< T, U > map_; 
}; 

Map< int, std::string >& g_map = Map< int, std::string> (1, "lorem") 
                 (3, "ipsum") 
                 (5, "dolor"); 

int main(int argc, char* argv[]) 
{ 
    std::cout << g_map[3] << std::endl; 
    return 0; 
} 

......产生这种损坏的输出?...

>g++ -g main.cpp 
>./a.out 
ipsumÿÿÿÿlorem!h€Ap€AD€A!ˆ€A¼gì¿P€A€A,€A!p€A€AY 

我了解到最近,分配给一个参考一个匿名的右值扩展了右值对象的生命周期。所以我认为由于匿名权值std::map被全局范围g_map引用,所以它的生命周期将被扩展到全局范围变量的范围,并且将g_map作为任何其他全局变量(如果不是作为参考,匿名右值将在结束分号处死亡)。

有人可以解释一下终生延长规则是如何适用于上述的吗?

使用gcc编译4.9.2。

+0

[Works for me](http://rextester.com/HTWI33012),它的价值。 –

+0

啊,我知道发生了什么事。你的程序表现出未定义的行为:'g_map'是一个悬而未决的参考。 –

+0

@StoneThrow MS VS当然,只有该编译器允许临时隐式转换为左值 – Slava

回答

4

你基本上是这样:

class C { 
public: 
    C& detemporize() { return *this; } 
}; 

C& cr = C().detemporize(); 

创建临时C实例。然后调用一个方法,该方法返回C&引用。编译器不知道也不关心返回值是指同一个临时值;尽管它知道,但它很可能会返回对某个全球性,长期存在的对象的引用。

无论如何,cr最后指的是临时的,然后立即死亡,留下cr悬空。任何后续尝试使用它都会显示未定义的行为。

在您的代码中,Map::operator()扮演的角色是detemporize(),而g_map仍然是一个悬挂引用。

+0

你说的话是有道理的,但最近我被告知给临时对象分配一个引用“延长了它的寿命”。现在我不清楚这是否属实,如果这种延伸是多久的话。如果存在延长寿命这样的事情,我会希望在我/你的例子中临时性地“成为全球范围”,因为全局变量没有“关闭大括号”,所以我推断变量的寿命将会是“提升”到与真正的全球变量相同(即在程序退出时死亡)。 – StoneThrow

+0

虽然你并没有把一个临时的参考文件绑定到一个参考文献上,但你正在约束一个左值。 'const C&cr = C();'会以你描述的方式工作,延长'C()'创建的引用的生命周期。 –

+0

我有点不清楚为什么在我的例子中,你说我将_lvalue_绑定到引用。我的词汇可能是原始的,但我认为左值是“等号左边的一个变量”,我在这幅图中看不到这个对象。 – StoneThrow

2

我最近了解到,分配对匿名右值的引用扩展了右值对象的生存期。

这仅发生在你直接临时对象赋值给一个参考:

const obj &ref1 = obj(); // extends 
const obj &ref = somefuncthatreturnsobj(); // extends 

但没有魔法存在,如果您调用隐藏了参考不知它不工作了一个功能:

class foo { 
    const foo &get() const { return *this; }; 
}; 

const foo &ref1 = foo(); // extends lifetime of temporary 
const foo &ref2 = foo().get(); // no lifetime extention, dangling reference 
+0

C++标准中有没有解释/证明这种行为的东西?我觉得在临时调用一个函数还是另一个暂时的...不是? – StoneThrow

+0

@StoneThrow如果对表达式类别使用正确的术语,则会更加清楚......当引用仅与* prvalue *绑定时,会出现生命周期扩展。对返回左值引用的函数的函数调用是*左值*。 –

+2

@StoneThrow考虑:'class Map {static longLivedInstance; Map&operator()(){return longLivedInstance; }}; Map&ref = Map()();'在这里,调用一个临时函数返回一个对那个暂时以外的东西的引用;而方法的签名与'return * this;'完全相同。你基本上期望编译器通过检查函数的实际实现来猜测你的意思。这甚至没有帮助 - 想象一下'返回rand()%2的方法吗? longLivedInstance:* this;' –