Какая на практике разница между LVALUE и RVALUE в следующем коде, когда я передаю текст? Я имею в виду, что в этом конкретном случае строки (где строка является строковым литералом) есть ли какая-либо польза от использования RVALUE (&&)?
void write_Lvalue(const std::string &text) {
//...
}
void write_Rvalue(const std::string &&text) {
//...
}
int main() {
write_Lvalue("writing the Lvalue");
write_Rvalue("writing the Rvalue");
}
В этом случае никакой выгоды. write_Rvalue
будет принимать только rvalue. и write_Lvalue
будет принимать только lvalue.
Когда вы передаете строковый литерал, временный std::string
будет создан из строкового литерала. Вариант rvalue уже может привязываться к этому, потому что вы уже передаете временное, а вариант lvalue может привязываться к временному, потому что это const
.
Это, например, не будет компилироваться:
void write_Lvalue(const std::string &text) {
//...
}
void write_Rvalue(const std::string &&text) {
//...
}
int main() {
std::string a = "hello";
write_Rvalue(a);
}
потому что мы пытаемся передать lvalue a
функции, принимающей только rvalue.
Преимущество, которое можно получить с помощью типов rvalue, заключается в том, что их можно перемещать. Есть отличный пост о том, почему двигаться можно быстрее здесь.
Создание вашего rvalue const
побеждает его цель, как сказано в комментариях, потому что его больше нельзя переместить.
Во-первых, постоянные ссылки на rvalue не очень полезны, так как вы не можете их перемещать. Для работы движущегося значения нужны изменяемые ссылки.
Возьмем ваш исправленный пример:
void write_lvalue(std::string const& text) {
//...
}
void write_rvalue(std::string&& text) {
//...
}
int main() {
write_lvalue("writing the Lvalue");
write_rvalue("writing the Rvalue");
}
В данном случае это полностью эквивалентен. В этих двух случаях компилятор должен создать строку и отправить ее по ссылке:
int main() {
// equivalent, string created
// and sent by reference (const& bind to temporaries)
write_lvalue(std::string{"writing the Lvalue"});
// equivalent, string created
// and sent by reference (&& bind to temporaries)
write_rvalue(std::string{"writing the Rvalue"});
}
Итак, зачем нужна функция, которая принимает ссылки на rvalue?
Это зависит от того, что вы делаете со строкой. Изменяемую ссылку можно переместить из:
std::string global_string;
void write_lvalue(std::string const& text) {
// copy, might cause allocation
global_string = text;
}
void write_rvalue(std::string&& text) {
// move, no allocation, yay!
global_string = std::move(text);
}
Так зачем вообще использовать ссылку rvalue? Почему бы не использовать изменяемую ссылку lvalue?
Это связано с тем, что изменяемые ссылки lvalue не могут быть привязаны к временным объектам:
void write_lvalue_mut(std::string& text) {
// move, no allocation... yay?
global_string = std::move(text);
}
int main() {
std::string s = /* ... */;
write_lvalue_mut(std::move(s)); // fails
write_lvalue_mut("some text"); // also fails
}
Но изменяемая ссылка на rvalue может быть привязана к rvalue, как показано выше.
Изменяемые ссылки lvalue также не работают с const
lvalue и довольно грубы для пользователей (которые должны предполагать, что их переменная может быть изменена функцией).
FWIW
const std::string &&text
не следует использовать. Вы побеждаете цель ссылки rvalue, делая то, на что она ссылаетсяconst
. Если этоconst
, его нельзя переместить, так как это операция мутирования.