Подтвердить что ты не робот

Краткое описание запрограммированных правил свертывания ссылок: (1) A & & → A &, (2) A & && → A &, (3) A && & → A & и (4) A && && → A &&

Следующая ссылка предоставляет 4 формы обращения коллапса (если я прав, что это только 4 формы): http://thbecker.net/articles/rvalue_references/section_08.html.

Из ссылки:

  • а & и становится A &
  • а && & становится A &
  • а && и становится A &
  • а && & & становится A &&

Хотя я могу сделать обоснованное предположение, я хотел бы дать краткое объяснение обоснования каждого из этих правил сбрасывания ссылок.

Связанный вопрос, если бы я мог: Являются ли эти правила сбрасывания ссылок в С++ 11 внутренними с помощью таких утилит STL, таких как std::move(), std::forward() и т.п., в типичные случаи использования в реальном мире? (Примечание. Я задаю вопрос, используются ли правила сбрасывания ссылок в С++ 11, в отличие от С++ 03 или ранее.)

Я задаю этот связанный вопрос, потому что я знаю такие утилиты С++ 11 как std::remove_reference, но я не знаю, регулярно ли используются утилиты, связанные с ссылкой, такие как std::remove_reference, на С++ 11, чтобы избежать необходимость в правилах сбрасывания ссылок или их использование в сочетании с правилами сбрасывания ссылок.

4b9b3361

Ответ 1

Правила сбрасывания ссылок (за исключением A& & -> A&, который является С++ 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, правила вывода типа С++ означают, что 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. Это не позволит вам вызвать функцию, которая принимает значение non-const lvalue, поскольку значение x не может связываться с ссылкой на константу lvalue. Он может связываться с константой lvalue, поэтому, если Call использовал const&, мы могли бы вызвать Fwd с xvalue.

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

По ссылке rvalue

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

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

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

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

станд:: вперед

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

Ответ 2

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

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

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

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