Как построить объекты из вектора строки и избежать копирования? - PullRequest
0 голосов
/ 30 сентября 2018

Предположим, что 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 голосов
/ 01 октября 2018

Вместо копирования вы можете перемещаться.

Во-первых, существует проблема с использованием конструктора 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))
       {}

Наконец, вы должны сделать ваши классы Data и StrData подвижными (что они не являются неявными из-законструктор удаленных копий) или трюк итератора перемещения не сработает:

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

PS.

Делать классы недоступными для копирования целесообразно только в качестве инструмента отладки длянайти случайные копии.Особенно в случае Data, который копируется так же быстро, как и перемещается.

Кроме того, прием векторов в конструкторе весьма ограничен.Если вы можете использовать шаблоны, вы можете вместо этого использовать конструктор шаблонов, взяв вместо него пары итераторов.

...