Ссылка на Rvalue: почему значения rvalues ​​неявно перемещаются?

В статье Artima о ссылке на С++ rvalue (http://www.artima.com/cppsource/rvalue.html) есть слова: для чего нужно сказать move (x) вместо этого просто x при переходе к базовому классу. Это ключевая функция безопасности семантики перемещения, предназначенная для предотвращения случайного перемещения дважды из некоторой именованной переменной.

Я не могу думать о ситуации, когда такое двойное движение может выполнить. Можете ли вы привести пример этого? Другими словами, что пойдет не так, если все члены T&& будут ссылками rvalue, а не только ссылками?

Ответ 1

Рассмотрим этот сценарий:

void foo(std::string x) {}
void bar(std::string y) {}

void test(std::string&& str)
{
    // to be determined
}

Мы хотим вызвать foo с str, затем bar с str, оба с тем же значением. Лучший способ сделать это:

foo(str); // copy str to x
bar(std::move(str)); // move str to y; we move it because we're done with it

Было бы ошибкой сделать это:

foo(std::move(str)); // move str to x
bar(std::move(str)); // move str to y...er, except now it empty

Потому что после первого перемещения значение str не указано.

Таким образом, при разработке ссылок rvalue этот неявный ход не существует. Если бы это было так, наш лучший способ выше не работал бы, потому что первое упоминание о str было бы std::move(str).

Ответ 2

То, как я вижу это, состоит в том, что если бы мы имели некоторую ссылку rvalue x:

T&& x = ...;

и мы вызвали некоторую функцию, используя x в качестве параметра:

f(x)

Нам нужно как-то сказать f, может ли он повредить x (или "взять на себя ответственность x", или "последний клиент использует x" ).

Одним из способов его разработки было бы квалифицировать каждый вызов:

f(yours_now(x)) // ok to damage
f(still_mine(x)) // dont damage

и сделать неквалифицированный вызов незаконным.

Другим способом было бы сделать один способ по умолчанию:

Или:

f(yours_now(x)) // ok to damage
f(x) // dont damage

или

f(x) // ok to damage
f(still_mine(x)) // dont damage

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

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

Во втором случае было нехорошо повредить объект, но мы случайно сказали, что это так. Это может привести к затруднению обнаружения логической ошибки в программе, так как x теперь находится в поврежденном состоянии по мере возврата f, но автор ожидал, что это не будет.

Итак, первый случай - это то, что было выбрано, потому что его "безопаснее".