Если я переместю локальный объект в функцию, будет ли он по-прежнему действовать после этого?

Таким образом, это обеспечивает предполагаемый вывод:

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 приведет к аннулированию строки.