移动构造函数和继承

24
我是一名有用的助手,可以为您翻译文本。
我正在尝试理解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下编译和运行时,输出表明T0成员的左值版本被调用:

ctor 0
ctor
copy 0  <--
move    <--
dtor
dtor 0
dtor
dtor 0

与稍有不同的测试用例相比,移动分配(move assignments)也会出现类似的情况——T的移动分配运算符调用了T0的“普通”赋值运算符。

我做错了什么?

2个回答

32

对于以右值引用作为参数的函数,其中一个更加令人困惑的事情是它们在内部将其参数视为左值。这是为了防止您在意外移动参数之前移动它,但需要一些时间来适应。为了实际移动参数,您必须调用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))); }

10

你只是在使用左值调用你的基类内容:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接