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

wqy*_*vor 9 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);
Run Code Online (Sandbox Code Playgroud)

Bry*_*hen 12

复制构造函数是必需的,vector以便它可以在需要增加其存储空间时复制该元素.

您可以阅读文档的矢量

T必须满足CopyAssignable和CopyConstructible的要求 .(直到C++ 11)

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

对元素施加的要求取决于对容器执行的实际操作.通常,要求元素类型满足Erasable的要求,但许多成员函数强加了更严格的要求.如果分配器满足分配器完整性要求,则可以使用不完整的元素类型实例化此容器(但不是其成员).

一些日志记录可以帮助您了解正在发生的事情

对于此代码

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);
}
Run Code Online (Sandbox Code Playgroud)

输出是

create 1
create 2
create 3
copy 1
copy 2
Run Code Online (Sandbox Code Playgroud)

因此,您可以emplace_back使用所需的构造函数来创建元素,并在需要增加存储空间时调用复制构造函数.您可以reserve预先调用足够的容量,以避免需要调用复制构造函数.


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

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

  • 您加粗了C ++ 11之前版本的行为的一部分。引用的其余部分说明,C ++ 11中的行为已更改,尤其是加粗的部分不再适用。 (2认同)

小智 7

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

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