С++ 0x: ссылка rvalue по сравнению с не константой lvalue

При программировании на С++ 03 мы не можем передать неназванный временный T() в функцию void foo(T&);. Обычное решение - дать временное имя, а затем передать его как:

T v;
foo(v);

Теперь, когда идет С++ 0x - и теперь с ссылками rvalue, функция, определенная как void foo(T&&), позволит мне передать временную. Это подводит меня к моему вопросу: поскольку функция, которая принимает ссылку rvalue, может принимать как ссылки rvalue (неназванные временные), так и ссылки lvalue (называемые неконстантными ссылками), есть ли какая-либо причина больше использовать ссылки lvalue в параметрах функции? Разве мы не должны всегда использовать rvalues ​​в качестве параметров функции?

Конечно, функция, которая принимает ссылку lvalue, не позволит вызывающему пользователю пройти временную, но я не уверен, что это полезное ограничение.

Ответ 1

", поскольку функция, которая принимает ссылку rvalue, может принимать как ссылки rvalue (неназванные временные), так и ссылки lvalue (называемые неконстантными ссылками)"

Это неправильный оператор. Во время первых итераций справочной спецификации rvalue это было правдой, но она больше не существует и реализована, по крайней мере, в MSVC, чтобы соответствовать этому более позднему изменению. Другими словами, это незаконно:

void f(char&&);

char x;
f(x);

Чтобы вызвать функцию, ожидающую ссылки rvalue с lvalue, вы должны превратить ее в rvalue следующим образом:

f(std::move(x))

Конечно, этот синтаксис достаточно четко показывает, какая разница между функцией, принимающей ссылку на lvalue, и ссылкой на rvalue на самом деле: ссылка на rvalue не должна пережить вызов. Это большое дело.

Теперь вы можете, конечно, создать новую функцию, которая делает именно то, что std:: move делает, а затем вы "можете" использовать ссылки rvalue вроде ссылок на lvalue. Я думал об этом, например, с картой посетителя, которую я имею, когда иногда вы просто не заботитесь о каком-либо результате обращения к посетителю, но в других случаях вы делаете и, следовательно, нуждаетесь в ссылке lvalue в этих случаях. С ссылкой на rvalue я мог получить оба... но это такое нарушение ссылочной семантики rvalue, что я решил, что это плохая идея.

Ваше выражение может быть путаницей, основанной на этом:

template < typename T >
void f(T&&);

char x;
f(x);

Это работает, но не потому, что вы передаете lvalue как ссылку rvalue. Он работает из-за ссылочного распада (также нового в С++ 0x). Когда вы передаете lvalue на такой шаблон, он фактически создается таким образом:

void f<char&>(char&&&);

Ссылочный распад говорит, что &&& превращается в &, поэтому фактическое создание экземпляра выглядит следующим образом:

void f<char&>(char&);

Другими словами, вы просто передаете lvalue по ссылке... ничего нового или особого в этом вопросе.

Надеюсь, что это очистит.

Ответ 2

Это полезное ограничение, если это временное должно быть активно удалено, например указатель на new память или ограниченный ресурс, такой как дескриптор файла. Но нужно передать эти запахи больше "плохого дизайна", чем "полезные ограничения".