是否可以使用const / reference成员创建唯一的可移动类?

时间:2016-03-23 16:32:36

标签: c++

一切都在问题中。我试过了:

#include <vector>

class Foo
{
    int & _x;

    public:
        Foo(int x) :
            _x(x)
        {}

        Foo(Foo && other) :
            _x(other._x)
        {}
        Foo & operator=(Foo && other) = default;

    private:
        Foo(Foo const &) = delete;
        Foo & operator=(Foo const &) = delete;
};

int main(void)
{
    int a, b;
    std::vector<Foo> vec;

    vec.push_back(Foo(a));
    vec.push_back(Foo(b));
    vec.erase(vec.begin());
    return 0;
}

但无法编译,因为默认情况下会删除Foo & operator=(Foo && other)

我试图删除此运算符,通过move强制编译器使用构造函数,但它尝试使用复制构造函数。

是否可以使用引用成员移动对象?

如果没有,为什么?

感谢。

编辑:我已经阅读了Move-assignment and reference member但它没有回答我的问题:我想知道编译器使用Foo & operator=(Foo const &)的原因,而Foo(Foo && other)可以使用。{/ p>

我无法理解为什么不能用引用成员移动一个类。

1 个答案:

答案 0 :(得分:3)

您的问题是erase要求元素类型为MoveAssignable。这仅仅是因为erase通常的实现方式:它在被擦除的元素(或元素)之后遍历向量,移动 - 分配给已擦除的插槽:

for (; j != end; ++i, ++j)
    *i = std::move(*j);
for (; i != end; ++i)
    i->~T();

这可以使用移动构造以不同的方式实现,但如果存在移动赋值运算符(大多数情况下)并且难以获得异常安全,那么这将是低效的:

for (; j != end; ++i, ++j)
{
    i->~T();
    new (&*i) T(std::move(*j));  // what if this throws?
}
for (; i != end; ++i)
    i->~T();

您可以通过使用其他策略来解决此问题,以获得erase的效果,例如:

vec = std::vector<Foo>(
  std::make_move_iterator(std::next(vec.begin())),
  std::make_move_iterator(vec.end()));

Foo仍然不会非常有用,正是因为具有引用成员的类型不能移动 - 可赋值(Move-assignment and reference member);您应该考虑是否可以使用reference_wrapper