Существует ли обратное к std::move?

std::move может использоваться, чтобы явно разрешить семантику перемещения, когда перемещение не было бы уже разрешено неявным образом (например, часто при возврате локального объекта из функции).

Теперь мне было интересно (особенно в контексте локального возврата и неявного перемещения туда), существует ли такая вещь, как обратное std::move это предотвратит перемещение объекта (но все же позволит копировать).

Имеет ли это смысл?

3 ответа

Решение

std::move преобразует lvalue в rvalue, и это делает это по существу static_cast, Наиболее близким к тому, что я могу считать противоположным, являются эти два типа приведений:

static_cast<T &>(/*rvalue-expression*/)
static_cast<const T&>(/*rvalue-expression*/)

Пример этого можно увидеть ниже:

#include <iostream>

void f(const int &)
{ std::cout << "const-lval-ref" << std::endl; }

void f(int &&)
{ std::cout << "rval-ref" << std::endl; }

int main()
{
  f(static_cast<const int &>(3));
  return 0;
}

Кастинг prvalue 3 в const int & гарантирует, что перегрузка lvalue f выбран.

В большинстве случаев вы получаете эту модификацию rvalue-to-lvalue автоматически, просто присваивая переменной:

int a = 3;

Когда вы используете a после этой строки это будет lvalue. Это правда, даже когда a объявляется как ссылка rvalue:

int &&a = 3;

Здесь тоже, a становится lvalue (в основном потому, что у него "есть имя").

Единственная ситуация, в которой я могу представить явное приведение к какому-либо значимому эффекту, это мой первый пример выше. И там, когда имеем дело с такими ценностями, как 3 или временные значения, возвращаемые при вызовах функций путем копирования, единственное допустимое приведение - это const-ссылка (не-const-ссылка не может привязываться к prvalue).

template<class T>
T& unmove(T&& t)
{
    return t;
}

Это изменит категорию значения выражения аргумента на lvalue, независимо от того, каким оно было изначально.

void f(const int&); // copy version
void f(int&&); // move version

int main()
{
    int i = ...;

    f(42); // calls move version
    f(move(i)); // calls move version
    f(i); // calls copy version

    f(unmove(42)); // calls copy version
    f(unmove(move(i))); // calls copy version
    f(unmove(i)); // calls copy version
}

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

Пример с ходом:

enter code here

#include <iostream>

class A {
public:
    std::string s;
    A() : s("test") {}
    A(const A& o) : s(o.s) { std::cout << "move failed!\n";}
    A(A&& o) : s(std::move(o.s)) { std::cout << "move was succesfull!\n"; }
};

int main(int argc, const char * argv[])
{
    A firsObject;
    A secondObject = std::move(firsObject);
    return 0;
}

Пример с отключенным движением:

#include <iostream>

class A {
public:
    std::string s;
    A() : s("test") {}
    A(const A& o) : s(o.s) { std::cout << "move failed!\n";}

private:
    A(A&& o) : s(std::move(o.s)) { std::cout << "move was succesfull!\n"; }
};

int main(int argc, const char * argv[])
{
    A firsObject;
    A secondObject = std::move(firsObject);
    return 0;
}
Другие вопросы по тегам