C ++ vector emplace_back调用复制构造函数

时间:2016-11-07 03:17:42

标签: c++ vector std

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

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);

3 个答案:

答案 0 :(得分:11)

vector需要复制构造函数,以便在需要增加存储空间时复制元素。

您可以阅读vector

的文档
  

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

     

强加的要求   元素取决于对其执行的实际操作   容器。通常,要求元素类型是完整的   键入并满足Erasable的要求,但许多成员函数   施加更严格的要求。 (自C ++ 11起)(直到C ++ 17)

     

在   对元素施加的要求取决于实际要求   在容器上执行的操作。通常,它是必需的   元素类型符合Erasable的要求,但很多成员   功能强加了更严格的要求。这个容器(但不是它的   如果是,则可以使用不完整的元素类型实例化成员   allocator满足分配器完整性要求。

某些日志记录可以帮助您了解正在进行的操作

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::vector,因为list被实现为链接列表,它没有& #39; t需要移动元素。

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:0)

根据https://en.cppreference.com/w/cpp/container/vector/emplace_backvalue_typestd::vector<T> 需要为MoveInsertableEmplaceConstructibleMoveInsertable 尤其需要移动构造函数或复制构造函数。

所以,如果你不想你的类被复制,你应该添加一个显式的移动构造函数。您可以使用 = default 来使用编译器提供的仅移动所有字段的默认实现。

完整示例

#include <vector>

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

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

注意

请注意,您可能会感到非常困惑

<块引用>

error: use of deleted function ‘MyClass::MyClass(const MyClass&)

当你使用 C++17 时

auto x = list.emplace_back(MyClass::e1);

代替

auto& x = list.emplace_back(MyClass::e1);

即使使用移动构造函数。