【问题标题】:std::vector doesn't call move constructor with objects with noexcept move constructsstd::vector 不调用带有 noexcept 移动构造的对象的移动构造函数
【发布时间】:2023-04-08 21:10:01
【问题描述】:

我最近一直在为移动和复制构造函数而苦苦挣扎,似乎无法自己找到遮篷。 结构相当简单。一个 OWUP 类,它持有一个对象的 std::unique_ptr(在这个例子中是一个 int),一个类 One 是 OWUP 的 std::vector 的简单包装器和类 2,它是 One 的简单包装器 std::vector .

#include <memory>
#include <vector>

class OWUP {
public:
    OWUP()
    : data(nullptr)
    { }

    OWUP(const OWUP &) = delete;
    OWUP &operator=(const OWUP &) = delete; 
    OWUP(OWUP &&) noexcept = default;
    OWUP &operator=(OWUP &&) noexcept = default; 

    std::unique_ptr<int> data; 
};

class One {
public:
    One(std::size_t numof_datas)
    : datas(numof_datas)
    { }

    One(const One &) = delete;
    One &operator=(const One &) = delete; 
    One(One &&) noexcept = default;
    One &operator=(One &&) noexcept = default; 

    std::vector<OWUP> datas;
};

class Two {
public:
    Two(std::size_t numof_ones, std::size_t num_of_datas)
    : ones(numof_ones, One(num_of_datas))
    { }

    Two(const Two &) = delete;
    Two &operator=(const Two &) = delete; 
    Two(Two &&) noexcept = default;
    Two &operator=(Two &&) noexcept = default;

    std::vector<One> ones;
}; 

代码得到以下错误代码g++ -std=c++14 example.cpp

In file included from /usr/include/c++/7.3.1/memory:64:0,
                 from example.cpp:1:
/usr/include/c++/7.3.1/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = One; _Args = {const One&}]':
/usr/include/c++/7.3.1/bits/stl_uninitialized.h:210:18:   required from 'static _ForwardIterator std::__uninitialized_fill_n<_TrivialValueType>::__uninit_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = One*; _Size = long unsigned int; _Tp = One; bool _TrivialValueType = false]'
/usr/include/c++/7.3.1/bits/stl_uninitialized.h:255:17:   required from '_ForwardIterator std::uninitialized_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = One*; _Size = long unsigned int; _Tp = One]'
/usr/include/c++/7.3.1/bits/stl_uninitialized.h:366:39:   required from '_ForwardIterator std::__uninitialized_fill_n_a(_ForwardIterator, _Size, const _Tp&, std::allocator<_Tp2>&) [with _ForwardIterator = One*; _Size = long unsigned int; _Tp = One; _Tp2 = One]'
/usr/include/c++/7.3.1/bits/stl_vector.h:1337:33:   required from 'void std::vector<_Tp, _Alloc>::_M_fill_initialize(std::vector<_Tp, _Alloc>::size_type, const value_type&) [with _Tp = One; _Alloc = std::allocator<One>; std::vector<_Tp, _Alloc>::size_type = long unsigned int; std::vector<_Tp, _Alloc>::value_type = One]'
/usr/include/c++/7.3.1/bits/stl_vector.h:298:27:   required from 'std::vector<_Tp, _Alloc>::vector(std::vector<_Tp, _Alloc>::size_type, const value_type&, const allocator_type&) [with _Tp = One; _Alloc = std::allocator<One>; std::vector<_Tp, _Alloc>::size_type = long unsigned int; std::vector<_Tp, _Alloc>::value_type = One; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<One>]'
example.cpp:40:39:   required from here
/usr/include/c++/7.3.1/bits/stl_construct.h:75:7: error: use of deleted function 'One::One(const One&)'
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
example.cpp:29:3: note: declared here
   One(const One &) = delete;
   ^~~

我已尽力强制它使用移动构造函数。我还尝试使用 std::move 创建自己的移动构造函数,但这导致了相同的编译错误。我知道 std::vector 对 move_if_noexcept() 进行测试,但无法找出原因。我做错了什么?

【问题讨论】:

    标签: c++ c++14 copy-constructor move-semantics move-constructor


    【解决方案1】:

    因为问题就在这里:

    Two(std::size_t numof_ones, std::size_t num_of_datas)
    : ones(numof_ones, One(num_of_datas))
    { }
    

    你不能从你在这里创建的One 移动,你真的需要从它复制numof_ones 次。

    【讨论】:

    • 嗯我不知道我是否完全理解你的遮阳篷,但删除 One(num_of_datas) 并在循环中手动添加 One(num_of_datas) numof_ones 次数确实可以解决问题。这是你的意思吗?
    • @73nismit 您正在调用向量的构造函数来复制参数。来自here - 2) 使用计数 copies 且值为 value 的元素构造容器。.
    • @73nismit 移动一个物体就像把一个苹果从一个盒子移到另一个盒子。结果是最后一个盒子看起来像移动之前的第一个盒子;但是你不需要去商店买更多的苹果。如果您只需要 1 个苹果,这非常有用。如果你需要 5 个苹果装在 5 个盒子里;你需要得到更多;在这种情况下,您需要复制该框。
    • 另外,并不是说删除了move构造函数,而是删除了copyconstructor。在“移动”和“复制”上切换位置?
    • @UKmonkey 我不想复制或移动开始,我想初始化二维数组的大小
    【解决方案2】:

    不幸的是,如果One 没有默认构造函数或复制构造函数,就没有简单的方法来构造大小为numof_onesstd::vector&lt;One&gt;。您选择的构造函数将 numof_ones 复制 您给它的单个值放入向量的每个元素中。

    最简单的解决方案可能是不在构造函数初始化器列表中初始化ones,而是在构造函数体中初始化它:

    Two(std::size_t numof_ones, std::size_t num_of_datas) {
        ones.reserve(numof_ones);
        for (size_t i = 0; i != numof_ones; ++i) {
            ones.emplace_back(num_of_datas);
        }
    }
    

    (从技术上讲,您可能会使用 std::vector 构造函数,该构造函数需要两个迭代器并提供您自己的自定义迭代器,但这可能不值得)。

    【讨论】:

    • 这是我为防止编译器错误所做的,但我认为这不是正确的方法。感觉不对。
    猜你喜欢
    • 2018-03-12
    • 2019-12-09
    • 2011-05-22
    • 2023-03-28
    • 2011-11-25
    • 2015-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多