2013-06-21 28 views
5

虽然问this question,了解到常量引用的临时对象是在C时有效++破参考refnop指的是一个被破坏的临时对象。我想知道为什么?常量引用临时对象变为后功能范围(寿命)

#include <string> 
#include <map> 

struct A 
{ 
    // data 
    std::map <std::string, std::string> m; 
    // functions 
    const A& nothing()   const { return *this; } 
    void init()      { m["aa"] = "bb"; } 
    bool operator!= (A const& a) const { return a.m != m; } 
}; 

int main() 
{ 
    A a; 
    a.init(); 

    A const& ref = A(a); 
    A const& refnop = A(a).nothing(); 

    int ret = 0; 
    if (a != ref)  ret += 2; 
    if (a != refnop) ret += 4; 

    return ret; 
} 

使用GCC 4.1.2和MSVC 2010测试,它返回4;

$> g++ -g refnop.cpp 
$> ./a.out ; echo $? 
4 

refrefnop之间的区别是调用nothing()这确实没什么好说的。在这次调用之后,临时对象被破坏!

我的问题:
为什么在refnop的情况下,临时对象的生命周期是不一样的const引用?

+0

注意:使用克++版本4.4和4.6,这段代码返回0 ... – olibre

回答

9

临时对象的生命周期扩展可以仅执行一次,当临时对象被绑定到所述第一参考。在那之后,引用引用临时对象的知识就消失了,所以进一步的生命周期扩展是不可能的。

是令人费解的,你

A const& refnop = A(a).nothing(); 

的情况与此类似的情况:

A const& foo(A const& bar) 
{ 
    return bar; 
} 
//... 
A const& broken = foo(A()); 

在这两种情况下,暂时被绑定到函数参数(用于nothing()隐含thisbarfoo()),并得到它的寿命“扩展”到函数的参数的寿命。我把引号“扩展”,因为临时的自然寿命已经较长,所以没有实际的扩展发生。

由于生命周期扩展属性是非传递性的,因此返回引用(恰好引用临时对象)不会进一步延长临时对象的生命周期,结果refnopbroken最终引用不再存在的对象。

1

我原来的例子是复杂的。

所以我在这里发布一个简单的例子,我提供相应的ISO C++ standard段落。

这个简单的例子也可在coliru.stacked-crooked.com/

#include <iostream> 

struct A 
{ 
    A(int i) { std::cout<<"Cstr "<< i<<'\n'; p = new int(i); } 
~A()  { std::cout<<"Dstr "<<*p<<'\n'; delete p;  } 

    const A& thiz() const { return *this; } 

    int *p; 
}; 

const A& constref(const A& a) 
{ 
    return a; 
} 

int main() 
{ 
    const A& a4 = A(4); 
    const A& a5 = A(5).thiz(); 
    const A& a6 = constref(A(6)); 

    std::cout << "a4 = "<< *a4.p <<'\n'; 
    std::cout << "a5 = "<< *a5.p <<'\n'; 
    std::cout << "a6 = "<< *a6.p <<'\n'; 
} 
使用命令行 g++-4.8 -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

输出:

Cstr 4 
Cstr 5 
Dstr 5 
Cstr 6 
Dstr 6 
a4 = 4 
a5 = 0 
a6 = 0 
Dstr 4 

正如你所看到的,通过a5a6引用的临时对象的析构的分别的功能thizconstref端。

这是§12.2临时对象,其中粗体部分适用于这种情况的提取物:

的第二上下文是当引用绑定到一个暂时的。 临时到该参考结合或临时 即一个子对象的完整的对象到所述基准 绑定持续基准的除了寿命:

  • 临时结合到基准构件在构造函数的 ctor-initializer(12.6.2)中一直存在,直到构造函数退出。
  • 临时绑定到函数调用(5.2.2) 中的引用参数,直到完成包含该调用的完整表达式。
  • 函数返回语句(6.6.3)中返回值 的临时绑定的生存期未被扩展;临时 在return语句中的完整表达式的末尾被销毁。
  • 临时结合到在新初始化(5.3.4)的参考持续 直到含有新初始化全表达的完成。

这是一个更完整的示例:

#include <iostream> 

struct A 
{ 
    A()   { std::cout<<"Cstr 9\n";   p = new int(v = 9);  } 
    A(int i) { std::cout<<"Cstr "<<i<<'\n'; p = new int(v = i);  } 
    A(const A&o){ std::cout<<"Copy "<<o.v<<'\n'; p = new int(v = 10+o.v); } 
    ~A()   { std::cout<<"Del "<<v<<' '<<*p<<'\n'; *p = 88; delete p; } 

    const A& thiz() const { return *this; } 

    int *p; 
    int v; 
}; 

const A& constref(const A& a) 
{ 
    return a; 
} 

std::ostream& operator<<(std::ostream& os, const A& a) 
{ 
    os <<"{ *p="<< *a.p <<" , v="<< a.v <<" }\n"; 
    return os; 
} 

int main() 
{ 
    std::cout << "---const A a1 = A(1)"    "\n"; 
        const A a1 = A(1); 
    std::cout << "---const A a2 = A(2).thiz()"   "\n"; 
        const A a2 = A(2).thiz(); 
    std::cout << "---const A a3 = constref(A(3))" "\n"; 
        const A a3 = constref(A(3)); 
    std::cout << "---const A& a4 = A(4)"    "\n"; 
        const A& a4 = A(4); 
    std::cout << "---const A& a5 = A(5).thiz()"   "\n"; 
        const A& a5 = A(5).thiz(); 
    std::cout << "---const A& a6 = constref(A(6))" "\n"; 
        const A& a6 = constref(A(6)); 

    std::cout << "a1 = "<< a1; 
    std::cout << "a2 = "<< a2; 
    std::cout << "a3 = "<< a3; 
    std::cout << "a4 = "<< a4; 
    std::cout << "a5 = "<< a5; 
    std::cout << "a6 = "<< a6; 
} 

和使用g++命令行相应的输出:

---const A a1 = A(1) 
Cstr 1 
---const A a2 = A(2).thiz() 
Cstr 2 
Copy 2 
Del 2 2 
---const A a3 = constref(A(3)) 
Cstr 3 
Copy 3 
Del 3 3 
---const A& a4 = A(4) 
Cstr 4 
---const A& a5 = A(5).thiz() 
Cstr 5 
Del 5 5 
---const A& a6 = constref(A(6)) 
Cstr 6 
Del 6 6 
a1 = { *p=1 , v=1 } 
a2 = { *p=12 , v=12 } 
a3 = { *p=13 , v=13 } 
a4 = { *p=4 , v=4 } 
a5 = { *p=0 , v=5 } 
a6 = { *p=0 , v=6 } 
Del 4 4 
Del 13 13 
Del 12 12 
Del 1 1