Кодировка Lvalue/rvalue -nes для универсальных ссылок

Я читал «Эффективный современный C++», и мое внимание привлекла следующая вещь:

В пункте 28 Скотт пишет:

Вместе эти наблюдения об универсальных ссылках и кодировании lvalue/rvalue означают, что для этого шаблона

template<typename T> void func(T&& param);

выведенный параметр шаблона T будет кодировать, является ли аргумент, переданный параметру, lvalue или rvalue. Механизм кодирования прост. Когда lvalue передается в качестве аргумента, T выводится как ссылка lvalue. Когда передается значение r, T выводится как не являющееся ссылкой. (Обратите внимание на асимметрию: lvalue кодируются как ссылки lvalue, а rvalue кодируются как не ссылки.)

Может кто-нибудь объяснить, почему был выбран такой механизм кодирования?

Я имею в виду, что если мы будем следовать правилам свертывания ссылок, то использование вышеупомянутого шаблона с rvalue даст ссылку на rvalue. И, насколько я могу судить, все будет работать точно так же, если бы это было выведено как ссылка rvalue. Почему он закодирован как нереференсный?


person Stvad    schedule 29.08.2015    source источник
comment
Кстати, термин универсальный справочник не прижился, и комитет решил использовать ссылка для пересылки вместо этого.   -  person Bo Persson    schedule 29.08.2015
comment
Я думаю, что ваш вопрос может быть таким же, как этот title="сбой при создании шаблонов функций из-за универсальной прямой ссылки"> stackoverflow.com/questions/32282705/   -  person aldr    schedule 29.08.2015
comment
См. open-std.org/jtc1/sc22. /wg21/docs/papers/2002/n1385.htm особенно. № 7. IIRC, Говард Хиннант однажды заявил, что введение некоторых &&& исключительно для ссылок на пересылку, вероятно, не будет принято в то время (в качестве третьего типа ссылки).   -  person dyp    schedule 29.08.2015
comment
OTOH, похоже, это не объясняет, почему T не выводится как A&&.   -  person dyp    schedule 29.08.2015


Ответы (2)


Допустим, вам нужно передать param. На самом деле, вам нужно сохранить его на всю жизнь. Как есть, вы бы просто использовали T:

template <typename T>
struct store { T val; };

template<typename T> void func(T&& param) {
    store<T> s{std::forward<T>(param)};
}

Это работает, потому что если бы param было передано через lvalue, T было бы ссылочным типом lvalue, а мы просто копируем ссылку. Если param передается через rvalue, нам нужно взять на себя ответственность — T не является ссылочным типом, поэтому в конечном итоге мы переходим в s.

Тот факт, что я могу просто использовать здесь T в качестве аргумента шаблона для store вместо std::conditional_t<std::is_lvalue_reference<T>::value, T, std::remove_reference_t<T>>, вероятно, не случаен.

person Barry    schedule 29.08.2015
comment
Я не понимаю вашего ответа. Если store<T> уничтожается после возврата func, то передача ссылок rvalue будет работать так же, как передача ссылок lvalue. Если store<T> сохраняется после возврата func, то ссылки lvalue также могут стать недействительными, как и ссылки rvalue. - person ; 29.08.2015
comment
Я думаю, дело было в том, что потом проще работать с T, чем с T&&, поэтому при условии, что мы можем достичь той же функциональности с T, он выводится как T. - person Stvad; 31.08.2015

Мне кажется, ты неправильно думаешь. Вместо того, чтобы спрашивать «почему бы не всегда делать T ссылочным типом», посмотрите на это с другой стороны:

Предположим, у вас есть template <typename T> void f(T&&t).

Называя его как int i; f(i);, необходимо некоторое T, чтобы T&& разрешалось в int&. Какой самый простой T может достичь этого? Это int&.

Называя его как f(0);, необходимо некоторое T, чтобы T&& разрешалось в int&&. Какой самый простой T может достичь этого? Это int, а не int&&.

int&& просто не имеет здесь никаких преимуществ.

person Community    schedule 29.08.2015