Существует ли обратное к 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;
}