Перемещение конструкторов и наследование

Я пытаюсь понять, как работают конструкторы перемещения и операции назначения в 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.

что я делаю не так?

2 ответов


одна из наиболее запутанных вещей о функциях, принимающих ссылки rvalue в качестве параметров, заключается в том, что внутренне они рассматривают свои параметры как lvalues. Это должно помешать вам переместить параметр до того, как вы это сделаете, но к этому нужно привыкнуть. Чтобы фактически переместить параметр, вы должны вызвать 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;
}