Существует ли инверсия std:: move?

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

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

Это даже имеет смысл?

Ответы

Ответ 1

std::move преобразует lvalue в r-значение, и он делает это в основном посредством 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;
}

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

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

int a = 3;

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

int &&a = 3;

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

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

Ответ 2

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

Пример с перемещением:

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;
}

Ответ 3

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
}