2016-11-07 45 views
2

这是一个演示类。我不希望我的类被复制,所以我删除了复制构造函数。我想vector.emplace_back使用这个构造函数'MyClass(Type type)'。但是这些代码不会编译。为什么?C++ vector emplace_back调用复制构造函数

class MyClass 
{ 
public: 
    typedef enum 
    { 
     e1, 
     e2 
    } Type; 
private: 
    Type _type; 
    MyClass(const MyClass& other) = delete; // no copy 
public: 
    MyClass(): _type(e1) {}; 
    MyClass(Type type): _type(type) { /* the constructor I wanted. */ }; 
}; 

std::vector<MyClass> list; 
list.emplace_back(MyClass::e1); 
list.emplace_back(MyClass::e2); 
+0

要解决该问题,请添加默认的移动构造函数。如果你不想移动,那么你的选择更有限 –

+0

谢谢,我选择写一个移动构造函数。 – wqyfavor

回答

5

拷贝构造函数是由vector必需的,这样它可以复制的元素时,它需要扩展其存储。

可以读取文档vector

T必须满足CopyAssignable的要求和 复制构造。 (直到C++ 11)

元素的要求取决于在容器上执行的实际操作。一般来说,要求元素类型是完整的 类型并且符合Erasable的要求,但许多成员函数 强加了更严格的要求。 (自C++ 11以来)(直到C++ 17)

对元素强加的 要求取决于在容器上执行的实际操作 。一般来说,要求 元素类型符合Erasable的要求,但许多成员函数强加更严格的要求。如果 分配器满足分配程序完整性要求,则此容器(但不包括其成员)可以使用不完整的元素类型实例化。

有些日志可以帮助你了解这是怎么回事

For this code

class MyClass 
{ 
public: 
    typedef enum 
    { 
     e1 = 1, 
     e2 = 2, 
     e3 = 3, 
    } Type; 
private: 
    Type _type; 
public: 
    MyClass(Type type): _type(type) { std::cout << "create " << type << "\n"; }; 
    MyClass(const MyClass& other) { std::cout << "copy " << other._type << "\n"; } 
}; 

int main() { 
    std::vector<MyClass> list; 
    list.reserve(2); 
    list.emplace_back(MyClass::e1); 
    list.emplace_back(MyClass::e2); 
    list.emplace_back(MyClass::e3); 
} 

输出

create 1 
create 2 
create 3 
copy 1 
copy 2 

所以你可以emplace_back并使用所需的构造函数来创建元素并在需要增加存储时调用复制构造函数。您可以拨打reserve并提供足够的容量以避免需要调用复制构造函数。


如果你真的不希望它被拷贝构造某种原因,你可以使用std::list,而不是作为std::vectorlist实现链表,它并不需要移动的元素。

http://coliru.stacked-crooked.com/a/16f93cfc6b2fc73c

+0

我是C++的新手,谢谢。 – wqyfavor

+0

您粗体显示了C++ 11之前的一部分行为。引用的其余部分解释了在C++ 11中更改的行为,特别是粗体部分不再适用。 –

+0

@Bryan Chen我认为你的意思是这个http://coliru.stacked-crooked.com/a/2dea811906d90d84 –

0

的问题只是一个精度。如果我们不希望在容器重新分配时使用对象复制构造,那么确实可以使用移动构造函数,但前提是它具有noexcept规范。

如果构造函数可能会抛出异常,容器会拒绝移动构造元素,因为它可能会导致容器处于无法清理的不良状态。这就是为什么当我们确信它永远不会抛出任何异常时,将移动构造器指定为noexcept通常是一种很好的做法。