为什么std :: reference_wrapper <const t =“”>不接受临时的?</const>

时间:2014-05-31 18:02:33

标签: c++ c++11 const rvalue reference-wrapper

通常,rvalues可以绑定到const引用(const SomeType&)。它内置于语言中。但是,std::reference_wrapper<const T>不接受rvalue作为其构造函数参数,因为故意删除相应的重载。这种不一致的原因是什么? std::reference_wrapper被“广告”作为参考变量的替代,用于我们必须按值传递但希望保留引用语义的情况。

换句话说,如果const &绑定的右值被认为是安全的,因为它内置于语言中,为什么C ++ 11的设计者不允许rvalues包含在std::reference_wrapper<const T>中?

什么时候会派上用场,你可能会问。例如:

class MyType{};

class Foo { 
public:
    Foo(const MyType& param){} 
};

class MultiFoo {
public:
    MultiFoo(std::initializer_list<std::reference_wrapper<const MyType>> params){} 
};

int main()
{
    Foo foo{MyType{}}; //ok
    MultiFoo multiFoo{MyType{}, MyType{}}; //error
}

4 个答案:

答案 0 :(得分:9)

将临时直接绑定到引用会延长其生命周期。

struct Foo {};
Foo f() { return {}; }

void g() {
    f(); // temporary destroyed at end of full-expression
    const Foo& r = f(); // temporary destroyed at end of scope
    // r can still be used here ...
    // ...
    // all the way down to here
}

但是,它必须将直接绑定到临时。请考虑以下示例:

struct Bar {
    Bar(const Foo& f): r(f) {}
    const Foo& r;
};

void h() {
    Bar b(f());
    // binding occurs through parameter "f" rather than directly to temporary "f()"
    // b.r is now a dangling reference! lifetime not extended
}

即使你可以,包裹一个临时包也是没用的。

注意:实际的引用包装器对象使用指针,以便可以重新分配:

struct Baz {
    Baz(const Foo& f): p(std::addressof(f)) {}
    Baz& operator=(const Foo& f) { p = std::addressof(f); return *this; }
    const Foo* p;
};

同样仍然适用:传递给构造函数或赋值运算符的临时函数将在包含调用的完整表达式的末尾被销毁。

答案 1 :(得分:8)

<强>引言

通常T const&T&&可以延长直接绑定到它的临时生命周期,但如果引用是&#34;隐藏&#34则不适用;在构造函数后面。

由于std::reference_wrapper是可复制的(按意图),如果使用std::reference_wrapper以使句柄转义创建临时的作用域,则引用对象的句柄可以比临时对象更长

这将导致句柄与引用的对象(即临时对象)之间的生命周期不匹配。


让我们玩&#34; 相信&#34;

想象一下,拥有以下非法的片段;我们假装std::reference_wrapper有一个接受临时的构造函数。

让我们假装传递给构造函数的临时代码将延长其生命周期(尽管事实并非如此,在现实生活中它将是&#34;死亡&#34;紧接着(1))。


typedef std::reference_wrapper<std::string const> string_ref;

string_ref get_ref () {
  string_ref temp_ref { std::string { "temporary" } }; // (1)

  return temp_ref; 
}

int main () {
  string_ref val = get_ref ();

  val.get (); // the temporary has been deconstructed, this is dangling reference!
}

由于使用自动存储持续时间创建临时,因此将在绑定到get_ref内的范围的存储上分配它。

get_ref稍后返回时,我们的临时将被销毁。这意味着val中的main会引用无效对象,因为原始对象不再存在。

以上是std::reference_wrapper构造函数没有接受临时值的重载的原因。


另一个例子

struct A {
  A (std::string const& r)
    : ref (r)
  { }

  std::string const& ref;
};

A foo { std::string { "temporary " } };

foo.ref = ...; // DANGLING REFERENCE!

std::string { "temporary" }的生命周期不会延长,正如标准中所述。

  

12.2p5 临时对象 [class.temporary]

     
    

引用绑定的临时值或作为绑定引用的子对象的完整对象的临时值在引用的生命周期内持续存在,但以下情况除外:

         
        
  • 构造函数的ctor-initializer(12.6.2)中的引用成员的临时绑定将持续存在,直到构造函数退出。

  •     
  • 函数调用(5.2.2)中的引用参数的临时绑定将持续到包含该调用的完整表达式完成为止。

  •     
  • 函数返回语句(6.6.3)中返回值临时绑定的生命周期未扩展;临时在return语句中的full-expression结束时被销毁。

             
          
    • new-initializer (5.3.4)中引用的临时绑定一直持续到包含 new-initializer 的完整表达式完成。
    •     
  •     
  

注意:重要的是要注意构造函数只不过是一个&#34;花哨的&#34; 函数,被调用对象构造。

答案 2 :(得分:1)

您不应复制const引用,因为它不一定使引用的对象保持活动状态。以下代码显示了问题:

#include <iostream>

struct X { int x; };

struct Foo {
    const X& a;
    Foo(const X& na) : a(na) {} // here na is still OK
};

int main()
{
    Foo foo{X{3}}; // the temporary exists only for this expression
    // now the temporary is no longer alive and foo.a is a dangling reference
    std::cout << foo.a.x << std::endl; // undefined behavior
}

const引用使X{3}的构造函数的临时Foo保持活动状态,但对于对象本身则不然。你得到一个悬空参考。

为了保护您免受此问题的影响,已禁用std::reference_wrapperstd::ref的临时对象。

答案 3 :(得分:1)

由于const T&&变量既不能被移动,也不能被移动,因此没有理由使用它(没有const T&的冒险或差异)。更重要的是,如果相应的临时不再存在,那么后面使用该引用可能是危险的(实际上,它是危险的,因为在这种情况下它会调用未定义的行为)。

毕竟删除它的rvalue-reference构造函数并不是一个坏主意。