Почему ссылка C++0x rvalue не используется по умолчанию?

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

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

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

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


person Zifre    schedule 09.05.2009    source источник
comment
Интересно, почему это получило 3 голоса, но 7 фаворитов. Я не думаю, что когда-либо добавлял вопрос в избранное, не проголосовав за него (если только у меня не было голосов или он был заблокирован).   -  person Zifre    schedule 12.05.2009
comment
Интересно, почему кто-то думает каким-то образом, а потом ожидает, что все остальные будут поступать точно так же, как он.   -  person user534498    schedule 05.04.2011
comment
Интересно, почему быть милым и голосовать за это необоснованное ожидание.   -  person Dan Nissenbaum    schedule 11.11.2012


Ответы (2)


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

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

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 (временное) в конце.


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

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

Теперь ты больше не можешь сказать

string() = "hello";

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

person Johannes Schaub - litb    schedule 09.05.2009
comment
+1 Вау, спасибо за последнюю часть о вызове члена только для lvalues ​​/ rvalues! Я много читал о C++0x, но ничего об этом не видел (думаю, это было в последнем черновике, но я не читал его полностью). Можете ли вы указать мне документацию об этой функции? - person Klaim; 10.05.2009
comment
Вот хороший обзор: open-std. org/jtc1/sc22/wg21/docs/papers/2005/n1821.htm . В рабочем документе см. 8.3.5, 9.3.1 и 13.3.1. - person Johannes Schaub - litb; 10.05.2009
comment
Спасибо за этот ответ, это делает его намного яснее. Я просто не очень хорошо понимал ссылки rvalue. Это имеет гораздо больше смысла, чем то, что я думал. - person Zifre; 10.05.2009
comment
Я хотел бы добавить, что thbecker.net/articles/rvalue_references/section_01.html это отличная ссылка на статью для чайников. - person rlbond; 10.05.2009
comment
@rlbond: это действительно отличная статья. - person Zifre; 10.05.2009
comment
rlbond, спасибо за хорошую статью. еще один замечательный: blogs.msdn.com/vcblog/archive/2009/02/03/ - person Johannes Schaub - litb; 10.05.2009
comment
Согласно текущим правилам, ваша функция toupper больше не будет компилироваться, потому что ссылки rvalue (возвращаемое значение) больше не могут быть инициализированы выражением (выражениями) lvalue. Кроме того, возвращение ссылок rvalue почти всегда является плохой идеей, поскольку это может привести к висячим ссылкам. Пример: строка const& foo = toupper(soandso); - person sellibitze; 21.10.2009
comment
@sellibitze, о, ты прав. Я думал, что это локально, поэтому в операторе return оно рассматривается как rvalue. Но я упустил, что это ссылочная переменная, поэтому она не рассматривается как rvalue. Спасибо, исправлю! Ваша вторая жалоба также обоснована. Я все исправлю. - person Johannes Schaub - litb; 21.10.2009
comment
Суффикс & интересен - я его не заметил. Круто, что это позволяет правильно применять древнее значение lvalue (что-то, что может появиться в левой части задания). - person Daniel Earwicker; 22.10.2009
comment
@litb: я думаю, что во 2-м функ. string toupper(string const& s), комментарий должен быть для остальных вызовов эталонной версии lvalue путем передачи копии lvalue. вместо rvalue. - person legends2k; 02.10.2010
comment
@legends имел в виду передачу копии lvalue. Эта копия является значением rvalue, заключенным путем передачи копии значения rvalue. - person Johannes Schaub - litb; 02.10.2010
comment
@litb: Ааа! Теперь я вижу это именно так! Старые привычки умирают с трудом :) - person legends2k; 02.10.2010
comment
@litb: Также спасибо за объяснение малоизвестных квалификаторов ссылок! - person legends2k; 04.10.2010

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

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

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

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

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

person Daniel Earwicker    schedule 09.05.2009