Пересылка ссылки с параметром шаблона класса [дубликат]

Я не совсем понимаю, каковы ваши требования, но в основном вы можете отдельно определить две переменные, а затем объединить их вместе.

$data1="The colour is ";
$data2="red";

$result=$data1.$data2;

Таким образом вы можете даже объявить $ data2 как глобальную чтобы вы могли изменить его значение во время выполнения, например, он мог получить ответ «красный» из флажка.

23
задан Dan Nissenbaum 5 December 2012 в 16:45
поделиться

2 ответа

Правила сбрасывания ссылок (за исключением A& & -> A&, который является C ++ 98/03) существуют по одной причине: позволить идеальной переадресации работать.

«Совершенная» пересылка означает эффективную пересылку параметры, как если бы пользователь вызывал функцию напрямую (минус elision, которая нарушена пересылкой). Существуют три типа значений, которые пользователь мог бы передать: lvalues, xvalues ​​и prvalues, и есть три способа, которыми принимающее местоположение может принимать значение: по значению, (возможно, const) lvalue reference и (возможно, const) rvalue ссылка

Рассмотрим эту функцию:

template<class T>
void Fwd(T &&v) { Call(std::forward<T>(v)); }

По значению

Если Call принимает свой параметр по значению, то копирование / перемещение должно происходить в этот параметр. Какой из них зависит от того, что такое входящее значение. Если входящее значение является значением l, то оно должно скопировать значение lvalue. Если входящее значение представляет собой rvalue (в совокупности это значения x и prvalues), то он должен перейти от него.

Если вы вызываете Fwd с lvalue, правила вывода типа C ++ означают, что T будет выведено как Type&, где Type является типом lvalue. Очевидно, что если lvalue const, оно будет выведено как const Type&. Правила сбрасывания ссылок означают, что Type & && становится Type & для v, ссылкой lvalue. Это именно то, что нам нужно назвать Call. Вызов с помощью ссылки lvalue заставит копию точно так же, как если бы мы ее вызывали напрямую.

Если вы вызываете Fwd с rvalue (то есть: временное выражение Type или определенное Type&& выражения), то T будет выводиться как Type. Правила ссылочного коллапса дают нам Type &&, что вызывает движение / копию, что почти так же, как если бы мы назовем его напрямую (минус elision).

По ссылке lvalue

Если Call берет свое значение с помощью ссылки lvalue, тогда его следует вызывать только тогда, когда пользователь использует параметры lvalue. Если это ссылка на const-lvalue, то она может быть вызвана чем угодно (lvalue, xvalue, prvalue).

Если вы вызываете Fwd с lvalue, мы снова получаем Type& как тип v. Это будет связываться с ссылкой на константу без ссылки. Если мы называем его константным Lvalue, мы получаем const Type&, которая будет связывать только с константным эталонным именующим аргументом в Call.

При вызове Fwd с xvalue, мы снова получаем Type&& как тип v. Это будет not позволяет вызывать функцию, которая принимает не const const lvalue, так как значение x не может связываться с ссылкой на константу lvalue. Он может привязываться к ссылке const lvalue, поэтому, если Call использовал const&, мы могли бы вызвать Fwd с xvalue.

Если вы вызываете Fwd с prvalue, мы снова получаем Type&&, поэтому все работает по-прежнему. Вы не можете передать временное значение функции, которая принимает неконстантное значение lvalue, поэтому наша функция пересылки будет также подавляться в попытке сделать это.

По ссылке rvalue

Если Call берет свое значение по ссылке rvalue, тогда его следует вызывать только тогда, когда пользователь использует параметры xvalue или rvalue.

Если вы вызываете Fwd с lvalue, мы получаем Type&. Это не будет связано с параметром ссылки rvalue, поэтому возникает ошибка компиляции. A const Type& также не привязывается к параметру ссылки rvalue, поэтому он все равно не работает. И это именно то, что произойдет, если мы назовем Call непосредственно с lvalue.

Если вы вызываете Fwd с xvalue, мы получаем Type&&, который работает (cv-qualification все еще имеет значение Конечно.

То же самое касается использования prvalue.

std :: forward

std :: forward сам использует правила сбрасывания ссылок аналогичным образом, чтобы передавать входящие значения rvalue в виде xvalues ​​(значения возвращаемой функции, которые являются Type&&, являются значениями x), а входящие значения lvalue как lvalues ​​(возврат Type&).

17
ответ дан Nicol Bolas 22 August 2018 в 17:37
поделиться
  • 1
    Отличный ответ. Я завис над этим: If you call Fwd with an lvalue, C++'s type-deduction rules mean that T will be deduced as Type&. Возможно, мое отсутствие понимания выходит за рамки комментария, и мне нужно изучить это больше (или задать другой вопрос). Но я не понимаю, почему вывод типа выберет T как Type&, а не Type. – Dan Nissenbaum 5 December 2012 в 17:59
  • 2
    @DanNissenbaum: Несколько причин, но самый простой из них наиболее очевидный: он не будет копировать значение (которое на самом деле не может быть скопировано). – Nicol Bolas 5 December 2012 в 18:34
  • 3
    Если T выводится как Type, то тип аргумента - Fwd() будет Type&&, правда, правильно? Наверное, это то, чего я не понимаю. – Dan Nissenbaum 5 December 2012 в 18:49
  • 4
    @DanNissenbaum: И тогда пересылка не будет perfect . Если я передаю lvalue в Fwd, Fwd нужно передать lvalue в Call. Fwd направляет Type&& как xvalue , а не lvalue. Таким образом, пересылка не идеальна. Кроме того, это правило является частью C ++ 98, где ссылки rvalue не существуют. – Nicol Bolas 5 December 2012 в 19:09
  • 5
    Как я уже думал об этом, ясно, что компилятор мог not выводить T как Type, если аргумент вызова Fwd является lvalue, потому что сигнатура функции тогда будет Fwd(Type &&), и компилятор не позволит передать lvalue функции, принимающей ссылку rvalue. Следующий возможный выбор заключается в том, что T является Type &, что соответствует сигнатуре функции Fwd(Type & &&), которая (согласно правилам сбрасывания ссылок) становится Fwd(Type &), которая является допустимой сигнатурой функции, поэтому она используется. – Dan Nissenbaum 5 December 2012 в 20:35

Правила на самом деле довольно просты. Rvalue reference является ссылкой на некоторое временное значение, которое не сохраняется за пределами выражения, которое его использует, - в отличие от lvalue reference, которое ссылается на сохраняющиеся данные. Поэтому, если у вас есть ссылка на сохраняющиеся данные, независимо от того, с какими другими ссылками вы ее совмещаете, фактические ссылочные данные являются lvalue - это охватывает первые три правила. 4-е правило также является естественным - ссылка rvalue на ссылку rvalue по-прежнему является ссылкой на непостоянные данные, поэтому дается ссылка rvalue.

Да, утилиты C ++ 11 полагаются на эти правила, реализация предоставленная вашей ссылкой, соответствует реальным заголовкам: http://en.cppreference.com/w/cpp/utility/forward

И да, рушительные правила вместе с аргументом шаблона правило дедукции применяется при использовании утилит std::move и std::forward, как описано в вашей ссылке.

Использование типов типов, таких как remove_reference, действительно зависит от ваших потребностей; move и forward для наиболее случайных случаев.

6
ответ дан SomeWittyUsername 22 August 2018 в 17:37
поделиться
  • 1
    В случае 3, однако, у вас есть A&& &, поэтому вы и начинаете с " со ссылкой на временное значение (A&&), а затем взять ссылку на него (A&& &), так что бы это не не было в категории 'if you have a reference to a persisting data, no matter what other references you combine it with...' (так как вы не начинаете со ссылкой на сохраняющиеся данные)? – Dan Nissenbaum 5 December 2012 в 19:59
  • 2
    @DanNissenbaum это хорошее наблюдение. Я полагаю, что, несмотря на отправную точку в качестве ссылки на rvalue, компилятор проверяет все выражение, чтобы принять более обоснованное решение. Связь между & amp; и & amp; & amp; является логическим and между false (& amp;) и true (& amp; & amp;). – SomeWittyUsername 5 December 2012 в 20:06
  • 3
    & Quot; & Quot-значение; является категорией выражений, невозможно описать «фактические ссылочные данные», как lvalue. Также ссылки rvalue могут относиться к не временным объектам (например, X&& y = std::move(x);) – M.M 4 September 2017 в 21:16
Другие вопросы по тегам:

Похожие вопросы: