Если я переместю локальный объект в функцию, будет ли он по-прежнему действовать после этого?
Таким образом, это обеспечивает предполагаемый вывод:
void f(std::string&& s)
{
s += " plus extra";
}
int main(void)
{
std::string str = "A string";
f( std::move(str) );
std::cout << str << std::endl;
return 0;
}
Строка плюс дополнительная
То есть, он работает, когда я запускаю его на Ideone, но это UB? Добавление дополнительных инициализаций строк до и после вызова f
ничего не изменило.
Ответы
Ответ 1
Код действителен, потому что фактическое перемещение не выполняется. Вот как вы можете сделать это недействительным:
string f(std::string&& s) {
std::string res(std::move(s));
res += " plus extra";
return res;
}
Состояние str
после того, как этот вызов будет действителен, но не указан. Это означает, что вы все равно можете назначить новое значение str
, чтобы вернуть его в допустимое состояние, но вы не сможете его выводить без вызова неуказанного поведения (). См. этот Q & A для специфики состояния перемещенного состояния.
Ответ 2
Это действительно, а не UB.
Это также ужасно запутанный код. std::move(s)
- это не что иное, как приведение к rvalue. Сам по себе он вообще не генерирует никакого кода. Его единственная цель - превратить значение lvalue в rvalue, чтобы клиентский код мог перегрузить выражения lvalue/rvalue (в этом случае string
).
Вы должны передать lvalue-reference для этого случая:
void f(std::string& s)
{
s += " plus extra";
}
...
f( str );
Или, наоборот, перейдите по значению и верните новую строку:
std::string f(std::string s)
{
s += " plus extra";
return s;
}
...
str = f( std::move(str) );
Ответ 3
std::move
ничего не двигает. Указывает, что объект может быть "перемещен из", передав свой аргумент в rvalue.
Ваш код действителен и не выполняется операция перемещения.
Вы получите поведение, когда состояние str
не определено после вызова f()
, если вы перемещаете-строите другой объект строки из s
. Перемещение-конструктор выполняет фактическую операцию перемещения.
Пример:
std::vector<std::string> sv;
void f(std::string&& s)
{
s += " plus extra";
sv.push_back(std::move(s)); // move s (str) into a new object
}
int main(void)
{
std::string str = "A string";
f(std::move(str));
std::cout << str << std::endl; // output: empty string
std::cout << sv.back() << std::endl; // output: "A string plus extra"
return 0;
}
Ответ 4
std::move
просто передает ваш объект в ссылку rvalue. Поскольку ваша функция берет ссылку и что-то делает с ней, здесь нет права собственности, поэтому ваша строка все еще находится в допустимом состоянии и может быть безопасно использована.
Я бы не рекомендовал использовать это в вашем коде, потому что он вводит в заблуждение, так как многие люди считают вашу строку недопустимой, поскольку использование права собственности является основным использованием ссылки rvalue, следовательно std::move
.
Если вам действительно нужно вызвать эту функцию таким образом, я бы рекомендовал написать это:
std::string myString{"a string"};
// leave a comment here to explain what you are doing.
f(static_cast<std::string&&>(myString));
Однако, обратите внимание, что ваш пример будет действительно другим, если функция f
приняла значение вместо ссылки. В этом случае вызов его с помощью std::move
или static_cast
приведет к аннулированию строки.