如何从字符串向量构造对象并避免复制?

时间:2018-09-30 20:51:49

标签: c++ copy-constructor

假设MyClass有两个std::vector类型的成员变量,一个拥有Data类型的结构,另一个拥有StrData(包含字符串)成员变量。我删除了默认和复制构造函数,以防止编译器不必要的复制:

struct Data {
    int x;
    float y;
    Data(int x, float y) : x(x), y(y) {}
    Data() = delete;
    Data(const Data& other) = delete;
};


struct StrData {
std::string xstr;
    StrData(std::string x) : xstr(x){}
    StrData() = delete;
    StrData(const StrData& other) = delete;
};


class MyClass {
    std::vector<Data> m_vect_data;
    std::vector<StrData> m_vect_str_data;
public:
    MyClass(const std::vector<Data> &m_vect_data,
            const std::vector<StrData> &m_vect_str_data) : m_vect_data(
            m_vect_data), m_vect_str_data(m_vect_str_data) {}
};

现在,我尝试使用以下初始化列表构造我的类:

MyClass s{{{1,2.0}, {2,4.0}}, {{"name"}}};

在编译此代码时,我抱怨:

/usr/include/c++/7/bits/stl_construct.h:75:7: error: use of deleted function ‘StrData::StrData(const StrData&)’

为什么编译器正确初始化了结构Data     仅包含整数类型(int和float),而不能初始化     第二个结构,即StrData,不需要复制构造器吗?

1 个答案:

答案 0 :(得分:2)

您可以移动而不是复制。

首先,存在一个问题,您正在使用向量的vector(std::initializer_list<T>, const Allocator&);构造函数。初始化程序列表只能被复制,因此对于不可复制的类型来说是不行的。

一种替代方法是,首先将列表声明为数组,创建一系列移动迭代器,然后改用迭代器构造器:

Data datas[] = {
    {1,2.0},
    {2,4.0},
};
StrData strs[] = {
    {"name"},
};
MyClass s{
    {std::make_move_iterator(std::begin(datas)), std::make_move_iterator(std::end(datas))},
    {std::make_move_iterator(std::begin(strs)), std::make_move_iterator(std::end(strs))},
};

现在,这还行不通,因为构造函数是从参数向量复制的。一种简单的解决方案是按值获取向量,并将其移动到成员中:

MyClass(std::vector<Data> m_vect_data,
        std::vector<StrData> m_vect_str_data
       ) : m_vect_data(std::move(m_vect_data)),
           m_vect_str_data(std::move(m_vect_str_data))
       {}

最后,您必须使您的DataStrData类可移动(由于删除了复制构造函数,所以它们不是隐式的),否则移动迭代器技巧将不起作用:

struct Data {
    // ...
    Data(Data&&);
};
struct StrData {
    // ...
    StrData(StrData&&);
};

PS。

使类不可复制仅作为查找意外副本的调试工具才有意义。特别是在Data的情况下,复制和移动都一样快。

此外,在构造函数中接受向量是非常有限的。如果可以使用模板,则可以改为使用带有一对迭代器的模板构造函数。