【问题标题】:Insert into vector having objects without copy constructor插入具有没有复制构造函数的对象的向量
【发布时间】:2013-11-18 12:48:45
【问题描述】:

我有一个类,其复制构造函数被显式删除(因为 A 在内部使用指针,我不想陷入浅拷贝陷阱):

class A {
  public:
    A(const A&) = delete;
    A& operator=(const A&) = delete;

    A(const B& b, const C& c);
}

现在我有一个 vector<A> aVector; 类型的向量,我想在其中插入元素 - 所以我使用 emplace_back

aVector.emplace_back(b, c);

但是,使用 gcc 编译失败,我得到了错误 -

third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...)
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:77:3:   required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator)  
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:119:41:   required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) 
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:260:63:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) 
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:283:67:   required from '_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&)
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/vector.tcc:410:6:   required from 'void std::vector<_Tp, _Alloc>::_M_emplace_back_aux(_Args&& ...) 
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/vector.tcc:102:4:   required from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...)

这个错误的原因是什么?如何在不删除复制构造函数的情况下修复它?我是否需要移动构造函数 - 是否需要显式定义?

【问题讨论】:

  • 你如何定义A(const B&amp; b, const C&amp; c);
  • 我自己也遇到过同样的问题,对我来说这是因为复制省略实现。必须提供 copy-ctor,但从未调用过。我刚刚测试了您的布局,并在我的 Mac 上使用 clang-500.2.79 遇到了与您在此处显示的相同的问题。提供一个 copy-ctor 允许它编译,但从不调用实现的 copy-ctor。同样提供一个 move-ctor。我必须清理问题历史才能找到相关问题。一旦你到达向量中的一个扩展点,移动构造将被调用(如果可用),所以无论如何你最好添加它。
  • 您应该添加移动构造函数 - 因为vectoremplace_back 可能会进行需要复制/移动构造函数的重定位。或者只使用std::deque

标签: c++ c++11 vector emplace


【解决方案1】:

您应该添加移动构造函数 - 因为std::vector::emplace_back 可能会进行需要复制/移动构造函数的重定位。或者直接使用std::deque

LIVE DEMO

#include <vector>
#include <deque>
using namespace std;

struct NoCopyNoMove
{
    NoCopyNoMove(const NoCopyNoMove&) = delete;
    NoCopyNoMove& operator=(const NoCopyNoMove&) = delete;
    NoCopyNoMove(NoCopyNoMove&&) = delete;
    NoCopyNoMove& operator=(NoCopyNoMove&&) = delete;

    NoCopyNoMove(int){};
};

struct OnlyMove
{
    OnlyMove(const OnlyMove&) = delete;
    OnlyMove& operator=(const OnlyMove&) = delete;
    OnlyMove(OnlyMove&&) noexcept {}
    OnlyMove& operator=(OnlyMove&&) noexcept {}

    OnlyMove(int){};
};

int main()
{
    deque<NoCopyNoMove> x;
    x.emplace_back(1);

    vector<OnlyMove> y;
    y.emplace_back(1);
}

§ 23.2.3 表 101 - 可选的序列容器操作

a.emplace_back(args) [...]

要求T 应该是 EmplaceConstructible 从 args 到 X。对于vectorT 也应将MoveInsertable 转换为X

【讨论】:

  • std::deque 在继承自的类的复制构造函数被删除时似乎不起作用。我无权访问我正在使用的源,但注释掉使用从该类添加对象到 std::vectorstd::deque 停止错误,"Error C2280 'ClassName::ClassNameConstructor(const ClassName &amp;)': attempting to reference a deleted function" std::list 也是如此。
  • 但是,我似乎能够用指针解决这个问题。我为每个对象制作了一个指针,并制作了一组这些指针。不是最好的解决方法,但我会采取有效的方法!
  • 读者应该注意,move-constructor和move-assignment上的noexcept说明符在这里很重要
【解决方案2】:

错误不是emplace_back的错。要将对象放入向量中,它必须是可移动的或可复制的。如果您实际运行代码并实现了复制构造函数,您会注意到它永远不会被调用。这是 cppreference.com 上的一个条目

我要解决这个问题是实现移动构造函数,使它可以编译,我看不出拥有移动构造函数有什么真正的缺点。与 cctor 一样,移动构造函数不会在您当前的代码中被调用。

【讨论】:

  • +1 将支持我所看到的一切。
  • @WhozCraig 我会找到一个标准报价,但有时那件事让我很头疼
  • FWIW,std::vectordocumentation 的最新版本不再提及元素是 MoveConstructible 或 MoveAssignable 的要求。他们只需要Erasable
【解决方案3】:

只想添加到@kayleeFrye_onDeck's answer。我的情况与他们的情况几乎相同,适用于我的确切语法(基于 cmets 中的反馈)如下:

vector< std::unique_ptr<ClassName> > names; // Declare vector of unique_ptrs of the class instance

std::unique_ptr<ClassName> name_ptr = std::make_unique<ClassName>();
names.push_back(std::move(name_ptr)); // Need to use std::move()

// Now you can access names objects without error:
names[0]->classMethod();

【讨论】:

    【解决方案4】:

    我在使用外部库的类时遇到了这个问题。我得到了,

    "Error C2280 ClassName::ClassName(const ClassName &amp;)': attempting to reference a deleted function"

    我猜我使用的类删除了它的复制构造函数。我无法将它添加到我知道的任何 std 容器中,用于我的自定义派生类对象,这些容器用我的一些助手包装了它们的对象以帮助进行初始化/错误检查。

    我使用 (risky) 指针解决了这个阻止程序。

    基本上,我过渡到这个:

    std::vector<ClassName*> names;
    ClassName name("arg");
    ClassName name_ptr = &name;
    names.push_back(name_ptr);
    

    从这里,原来:

    std::vector<ClassName> names;
    ClassName name("arg");
    names.push_back(name);
    

    有趣的是,这是我第一次使用 C++ 进行编码,实际上我需要将指针用于非指针特定的使用要求,因为没有已知的替代方法。这让我担心我可能会错过自己代码中的一些基本内容。

    也许有更好的方法来做到这一点,但它还没有在这个问题的答案列表中......

    编辑警告:

    我之前应该提过这个,谢谢aschepler; 如果你这样做并且你使用的容器比对象寿命长,“砰,你死了。”

    【讨论】:

    • 但是如果向量的生命周期长于你放入其中的对象的生命周期,砰,你死了。
    • 你确定。但是,嘿,我对替代方案非常开放 :) 我没有找到任何...再次感谢您提醒我将警告添加到我的答案中。
    • 您可以通过使用make_unique 创建的unique_ptr 向量来改进您的代码
    • 这就是你的意思,@M.M 通过将它与第一个 sn-p 的第 3 行交换出来? std::unique_ptr&lt;ClassName&gt; name_ptr = std::make_unique&lt;ClassName&gt;(name);
    猜你喜欢
    • 2021-12-04
    • 1970-01-01
    • 2011-08-22
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-29
    相关资源
    最近更新 更多