Обычно rvalues может связываться с константными ссылками (const SomeType&
). Это встроено в язык. Тем не мение, std::reference_wrapper<const T>
не принимает значение rvalue в качестве аргумента конструктора, поскольку соответствующая перегрузка намеренно удаляется. В чем причина этого несоответствия? 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
}
Привязка временного объекта непосредственно к ссылке продлевает срок его службы.
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;
};
То же самое относится и к следующему: временное значение, переданное в конструктор или оператор присваивания, будет уничтожено в конце полного выражения, содержащего вызов.
ВСТУПЛЕНИЕ
Обычно 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
позже возвращается, наш временный будет уничтожен. Это означает, что наш 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) не продлевается; временное уничтожается в конце полного выражения в операторе возврата.
- Временная привязка к ссылке в новый инициализатор (5.3.4) сохраняется до завершения полного выражения, содержащего новый инициализатор.
Заметка: важно отметить, что конструктор не более чем «Фантазия» функция, вызываемая при построении объекта.
Вы не должны копировать константную ссылку, так как она не обязательно поддерживает ссылочный объект живым. Следующий код показывает проблему:
#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_wrapper
а также std::ref
был отключен.
Так как const T&&
Переменная не может быть ни перемещена, ни изменена, нет причин для ее использования (нет никаких преимуществ или различий по сравнению с const T&
). Более того, последующее использование этой ссылки может быть опасным, если соответствующий временный объект больше не существует (на самом деле, это опасно, потому что это вызывает неопределенное поведение в таком случае).
Удаление его конструктора rvalue-reference не является плохой идеей.