首页 > 解决方案 > 明确要求默认移动构造函数不起作用?

问题描述

这是我的代码:

#include <cstdint>
#include <vector>

class Bar {
    uint32_t m_value;
public:
    Bar(const uint32_t value) : m_value(value) {
    }
};

class Foo {
    Bar* m_p_bar;
    uint32_t m_value;
    Foo(const Foo&) = delete;
    Foo& operator=(const Foo&) = delete;
    Foo& operator=(Foo&&) = default;
    Foo(Foo&&) = default;
public:
    /*
    Foo(Foo&& from) {
        m_p_bar = from.m_p_bar;
        m_value = from.m_value;
        from.m_p_bar = nullptr;
    }
    */
    Foo(const uint32_t value) : m_value(value) {
        m_p_bar = new Bar(value);
    }
};

int main() {
    std::vector<Foo> foos;
    foos.emplace_back(8);
}

编译器抱怨:

In file included from /opt/rh/devtoolset-9/root/usr/include/c++/9/vector:66,
                 from test_implicit_func.cpp:2:
/opt/rh/devtoolset-9/root/usr/include/c++/9/bits/stl_uninitialized.h: In instantiation of ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<Foo*>; _ForwardIterator = Foo*]’:
/opt/rh/devtoolset-9/root/usr/include/c++/9/bits/stl_uninitialized.h:307:37:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<Foo*>; _ForwardIterator = Foo*; _Tp = Foo]’
/opt/rh/devtoolset-9/root/usr/include/c++/9/bits/stl_uninitialized.h:329:2:   required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = Foo*; _ForwardIterator = Foo*; _Allocator = std::allocator<Foo>]’
/opt/rh/devtoolset-9/root/usr/include/c++/9/bits/vector.tcc:474:3:   required from ‘void std::vector<_Tp, _Alloc>::_M_realloc_insert(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {int}; _Tp = Foo; _Alloc = std::allocator<Foo>; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<Foo*, std::vector<Foo> >; typename std::_Vector_base<_Tp, _Alloc>::pointer = Foo*]’
/opt/rh/devtoolset-9/root/usr/include/c++/9/bits/vector.tcc:121:4:   required from ‘std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int}; _Tp = Foo; _Alloc = std::allocator<Foo>; std::vector<_Tp, _Alloc>::reference = Foo&]’
test_implicit_func.cpp:34:21:   required from here
/opt/rh/devtoolset-9/root/usr/include/c++/9/bits/stl_uninitialized.h:127:72: error: static assertion failed: result type must be constructible from value type of input range
  127 |       static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
      |                                                                        ^~~~~

我注意到由于某种原因,我需要为Foo. 通过明确要求编译器使用默认值(即Foo(Foo&&) = default;),它不起作用。但是,如果我要求编译器使用所有隐含的(即删除Foo(const Foo&) = delete; Foo& operator=(const Foo&) = delete; Foo& operator=(Foo&&) = default; Foo(Foo&&) = default;),那么编译器不会抱怨。

我的问题是为什么明确要求编译器在这里使用默认的移动构造函数不起作用,但在这种情况下它起作用。这个答案表明我可以要求默认版本,如果它被隐式删除。

标签: c++11

解决方案


如果您让编译器使用隐式定义的“big 5”,您将遇到泄漏和双重释放问题:s。

您需要创建移动构造函数public,并且需要delete指向析构函数中的指针。您还需要确保不要将指针留在移出对象中。

#include <utility> // exchange

class Foo {
    Bar* m_p_bar;
    uint32_t m_value;

public:
    Foo(const uint32_t value) : 
        m_p_bar(new Bar(value)),
        m_value(value) 
    {}

    Foo(const Foo&) = delete;
    Foo& operator=(const Foo&) = delete;

    Foo(Foo&& rhs) :
        m_p_bar(std::exchange(rhs.m_p_bar, nullptr)),  // put a nullptr in rhs
        m_value(rhs.m_value)
    {}
    Foo& operator=(Foo&& rhs) {
        std::swap(m_p_bar, rhs.m_p_bar);     // let rhs destroy our current Bar
        m_value = rhs.m_value;
        return *this;
    }

    ~Foo() { delete m_p_bar; }               // destroy it
};

一个更好的主意是使用std::unique_ptr<Bar>- 或根本不使用指针。


推荐阅读