为什么 std::reference_wrapper<const T> 不接受临时的?
Why does std::reference_wrapper<const T> not accept a temporary?
通常,右值可以绑定到const引用(const SomeType&
)。这是语言中固有的。然而,std::reference_wrapper<const T>
不接受右值作为其构造函数参数,因为相应的重载被故意删除了。这种不一致的原因是什么?当我们必须按值传递但希望保留引用语义时,std::reference_wrapper
被"宣传"为引用变量的替代方案。
换句话说,如果右值到const &
的绑定被认为是安全的,因为它是内置于语言中的,为什么c++ 11的设计者不允许在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
}
介绍
通常T const&
和T&&
可以延长直接绑定到它的临时对象的生命周期,但如果引用"隐藏"在构造函数后面,则不适用。
由于std::reference_wrapper
是可复制的(有意的),如果std::reference_wrapper
的句柄转义创建临时对象的作用域,那么被引用对象的句柄可以比临时对象存活得更久。
让我们玩"相信 "
想象一下下面的非法代码片段;这里我们假定std::reference_wrapper
有一个构造函数可以接受一个临时的。
让我们假设传递给构造函数的临时变量的生命周期将被延长(尽管事实并非如此,在现实生活中,它将在(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
稍后返回时,临时函数将被销毁。这意味着main
中的val
将引用一个无效的对象,因为原来的对象已经不存在了。
以上就是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]
引用绑定到的临时对象或作为引用绑定到的子对象的完整对象的临时对象在引用的生命周期内持续存在,除了:
在构造函数的参数初始化式(12.6.2)中,到引用成员的临时绑定一直存在,直到构造函数退出。
函数调用(5.2.2)中与引用形参的临时绑定一直持续到包含该调用的完整表达式完成为止。
在函数返回语句(6.6.3)中临时绑定到返回值的生存期不被延长;临时变量在返回语句的完整表达式结束时销毁。
- 到new-initializer(5.3.4)中的引用的临时绑定将一直存在,直到包含new-initializer的完整表达式完成为止。
注意:重要的是要注意构造函数只不过是一个"花哨的"函数,在对象构造时调用。
将临时对象直接绑定到引用可以延长其生存期。
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;
};
仍然适用:传入构造函数或赋值操作符的临时变量将在包含调用的完整表达式结束时销毁。
不应该复制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引用对Foo
的构造函数保持临时X{3}
存活,但对对象本身无效。你得到一个悬空引用。
为了保护您避免这个问题,std::reference_wrapper
和std::ref
的临时对象的使用已被禁用。
由于const T&&
变量不能移动,也不能修改,所以没有理由使用它(与const T&
相比没有优势或差异)。更重要的是,如果相应的临时对象不再存活,那么后置使用该引用可能是危险的(实际上,是危险的,因为在这种情况下它调用未定义的行为)。
删除右值引用构造函数毕竟是个好主意。
- 为什么线程不接受此输入?
- 函数不接受 X 参数,函数使用默认参数
- C++ boost::asio::ip::tcp::acceptor 有时不接受连接器?
- B不接受8作为输入的是什么?C++
- C++数组输入不接受一定数量的整数
- C++概念assignable_from不接受 const&-return 运算符=
- 斯堪夫不接受输入
- C++字符数组不接受超过 4 个字符的输入
- CIN不接受C++中带有空格的输入?
- 为什么C++ STL 哈希表 (unordered_map) 不接受向量作为键
- 为什么不接受具有默认分配参数的函数作为 0-arg 生成器?
- 模板接受常量,但不接受文字
- 如何在构造函数中传递 const 引用时强制编译器不接受右值
- 为什么 strtok_r() 只接受字符数组而不接受字符指针
- C++ cout 不接受字符串或带 + 的字符串
- std::cin不接受输入
- 带有openSSL的libwebsocket服务器不接受连接
- 函数不接受 C++ 参数
- 将任意数据传递给不接受"void* userarg"的C++回调
- C++ 实例化新对象时不接受继承方法默认参数值