Почему C ++ 0x rvalue ссылка не по умолчанию? - PullRequest
20 голосов
/ 10 мая 2009

Одной из интересных новинок грядущего стандарта C ++, C ++ 0x, являются «ссылки на значения». Ссылка rvalue похожа на ссылку lvalue (нормальную), за исключением того, что она может быть связана с временным значением (обычно временная ссылка может быть связана только с ссылкой const):

void FunctionWithLValueRef(int& a) {...}
void FunctionWithRValueRef(int&& a) {...}

int main() {
     FunctionWithLValueRef(5); // error, 5 is a temporary
     FunctionWithRValueRef(5); // okay
}

Итак, почему они изобрели совершенно новый тип, вместо того, чтобы просто снять ограничения на нормальные ссылки, чтобы позволить им быть привязанными к временным файлам?

Ответы [ 2 ]

43 голосов
/ 10 мая 2009

Это было бы бессмысленно. Вы изменили бы вещь в функции, и изменение было бы немедленно потеряно, потому что вещь была фактически временной.

Причина нового типа проистекает из необходимости уметь решать, что на самом деле является значением, а что нет. Только тогда вы сможете использовать их для классных вещей, которые они используют.

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, значение r может быть напрямую изменено, потому что мы знаем, что временное значение в любом случае выбрасывается, поэтому мы можем просто изменить его и не нужно копировать Это. Кроме того, то же самое наблюдение используется для вещи, называемой move-constructors и move-assignment. Правая сторона не скопирована, но ее вещи просто украдены и перемещены в *this.

Если бы вы сказали, что rvalue может связываться с неконстантными ссылками lvalue, то у вас не было бы способа выяснить, ссылается ли это на lvalue (именованный объект) или rvalue (временное) в конце.


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

struct string {
    string& operator=(string const& other) & { /* ... */ }
};

Теперь вы не можете больше говорить

string() = "hello";

Что сбивает с толку и в большинстве случаев не имеет смысла. То, что делает & выше, говорит о том, что оператор присваивания может быть вызван только для lvalue. То же самое можно сделать для rvalues, поставив &&.

12 голосов
/ 10 мая 2009

Поскольку добавление нового вида ссылки позволяет записать две перегрузки метода:

void CopyFrom(MyClass &&c)
{
    dataMember.swap(c);
}

void CopyFrom(const MyClass &c)
{
    dataMember.copyTheHardWay(c);
}

Версия, которая принимает новый тип ссылки, может изменять полученную переменную, поскольку эта переменная не будет использоваться где-либо еще. Так что он может «украсть» его содержимое.

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

...