Почему std :: reference_wrapper & lt; const T & gt; не принять временный?

Обычно 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
}

12

Решение

Привязка временного объекта непосредственно к ссылке продлевает срок его службы.

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;
};

То же самое относится и к следующему: временное значение, переданное в конструктор или оператор присваивания, будет уничтожено в конце полного выражения, содержащего вызов.

9

Другие решения

ВСТУПЛЕНИЕ

Обычно 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) сохраняется до завершения полного выражения, содержащего новый инициализатор.

Заметка: важно отметить, что конструктор не более чем «Фантазия» функция, вызываемая при построении объекта.

8

Вы не должны копировать константную ссылку, так как она не обязательно поддерживает ссылочный объект живым. Следующий код показывает проблему:

#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 был отключен.

1

Так как const T&& Переменная не может быть ни перемещена, ни изменена, нет причин для ее использования (нет никаких преимуществ или различий по сравнению с const T&). Более того, последующее использование этой ссылки может быть опасным, если соответствующий временный объект больше не существует (на самом деле, это опасно, потому что это вызывает неопределенное поведение в таком случае).

Удаление его конструктора rvalue-reference не является плохой идеей.

1
По вопросам рекламы [email protected]