Существует ли инверсия 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
}