Я пытаюсь понять, как работают конструкторы перемещения и операции назначения в C ++ 11, но у меня возникают проблемы с делегированием родительским классам.
Код:
class T0
{
public:
T0() { puts("ctor 0"); }
~T0() { puts("dtor 0"); }
T0(T0 const&) { puts("copy 0"); }
T0(T0&&) { puts("move 0"); }
T0& operator=(T0 const&) { puts("assign 0"); return *this; }
T0& operator=(T0&&) { puts("move assign 0"); return *this; }
};
class T : public T0
{
public:
T(): T0() { puts("ctor"); }
~T() { puts("dtor"); }
T(T const& o): T0(o) { puts("copy"); }
T(T&& o): T0(o) { puts("move"); }
T& operator=(T const& o) { puts("assign"); return static_cast<T&>(T0::operator=(o)); }
T& operator=(T&& o) { puts("move assign"); return static_cast<T&>(T0::operator=(o)); }
};
int main()
{
T t = std::move(T());
return 0;
}
Однако, когда я компилирую и запускаю под VS2012, выходные данные указывают, что версии lvalue членов T0 называются:
ctor 0
ctor
copy 0 <--
move <--
dtor
dtor 0
dtor
dtor 0
Аналогичная ситуация (с немного другим тестовым примером) происходит с назначениями перемещений — оператор назначения перемещений T вызывает «нормальный» оператор назначения T0.
Что я делаю неправильно?
Одна из самых запутанных вещей в функциях, принимающих rvalue-ссылки в качестве параметров, заключается в том, что внутренне они обрабатывают свои параметры как lvalue. Это сделано для того, чтобы вы не могли переместить параметр, прежде чем вы захотите, но к этому нужно привыкнуть. Чтобы реально переместить параметр, вы должны вызвать для него std :: move (или std :: forward). Итак, вам нужно определить ваш конструктор перемещения как:
T(T&& o): T0(std::move(o)) { puts("move"); }
и ваш оператор назначения перемещения как:
T& operator=(T&& o) { puts("move assign"); return static_cast<T&>(T0::operator=(std::move(o))); }
Вы только когда-либо называете вещи своего базового класса lvalues:
void foo(int&){} // A
void foo(int&&){} // B
void example(int&& x)
{
// while the caller had to use an rvalue expression to pass a value for x,
// since x now has a name in here it's an lvalue:
foo(x); // calls variant A
}
example(std::move(myinteger)); // rvalue for us, lvalue for example
То есть вам нужно:
T(T&& o):
T0(std::move(o)) // rvalue derived converts to rvalue base
{
puts("move");
}
А также:
T& operator=(T&& o)
{
puts("move assign");
T0::operator=(std::move(o)));
return *this;
}