Почему ссылка rvalue в C++0x не используется по умолчанию?
Вопрос
Одной из замечательных новых функций готовящегося к выходу стандарта C++, C++0x, являются «ссылки rvalue». Ссылка rvalue похожа на ссылку lvalue (normal), за исключением того, что она может быть привязана к временному значению (обычно временная ссылка может быть привязана только к const
ссылка):
void FunctionWithLValueRef(int& a) {...}
void FunctionWithRValueRef(int&& a) {...}
int main() {
FunctionWithLValueRef(5); // error, 5 is a temporary
FunctionWithRValueRef(5); // okay
}
Так почему же они изобрели совершенно новый тип вместо того, чтобы просто снять ограничения на обычные ссылки, чтобы позволить им привязываться к временным объектам?
Решение
Это было бы бессмысленно.Вы бы изменили объект в функции, и это изменение было бы немедленно потеряно, потому что на самом деле объект был временным.
Причина создания нового типа связана с необходимостью решать, что на самом деле является значением r, а что нет.Только тогда вы действительно сможете использовать их для тех крутых вещей, которые они используют.
string toupper(string && s) { // for nonconst rvalues
for(char &c : s) make_uppercase(c);
return move(s); // move s into a returned string object
}
string toupper(string const& s) { // for the rest
// calls the rvalue reference version, by passing
// an rvalue copy.
return toupper(string(s));
}
Теперь, если у вас есть какое-то значение rvalue и вы передаете его toupper, это значение rvalue можно изменить напрямую, потому что мы знаем, что временное значение в любом случае является выбрасываемым, поэтому мы также можем просто изменить его и не копировать его.Кроме того, то же наблюдение используется для вещей, называемых конструкторами перемещения и присваиванием перемещения.Правая часть не копируется, а ее элементы просто украдены и перенесены в *this
.
Если бы вы сказали, что значения rvalue могут привязываться к неконстантным ссылкам lvalue, то у вас не было бы возможности выяснить, ссылается ли это на lvalue (именованный объект) или на rvalue (временное) в конце.
Вероятно, об этом мало что известно, но в любом случае полезно: вы можете поместить ref-квалификаторы lvalue или rvalue в функцию-член.Вот пример, который естественным образом расширяет существующую семантику ссылок rvalue на неявный параметр объекта:
struct string {
string& operator=(string const& other) & { /* ... */ }
};
Теперь ты больше не можешь сказать
string() = "hello";
Это сбивает с толку и большую часть времени не имеет смысла.Что за &
выше говорит о том, что оператор присваивания может быть вызван только для lvalues.То же самое можно сделать и для rvalue, поставив &&
.
Другие советы
Потому что добавление нового типа ссылки позволяет вам написать две перегрузки метода:
void CopyFrom(MyClass &&c)
{
dataMember.swap(c);
}
void CopyFrom(const MyClass &c)
{
dataMember.copyTheHardWay(c);
}
Версия, которая принимает ссылку нового типа, может изменять полученную переменную, поскольку эта переменная больше нигде не будет использоваться.Таким образом, он может «украсть» его содержимое.
Это единственная причина, по которой была добавлена эта функция;сохранение одного типа ссылок не достигнет желаемой цели.