Массив неизвестной длины в списке инициализатора конструктора - PullRequest
2 голосов
/ 03 июля 2019

У меня есть класс с массивом членов.Длина является константой, но эта константа не известна до времени компиляции (в моем реальном коде эта константа определяется по-разному для разных целей компиляции).Тип массива - это класс без конструктора по умолчанию.

#define CONSTANT 2

class Data {
public:
    Data(int number){}
};

class DemoClass {
private:
    Data _member[CONSTANT];
public:
    DemoClass():
        _member{
            Data(0),
            Data(0)
        }
    {
        // stuff
    }
};

В этом примере я могу установить _member, используя список инициализатора.Однако, если значение COSNTANT изменится, мне придется изменить этот список инициализатора.

Теоретически, изменение DemoClass на конструктор по умолчанию, который вызывает другой конструктор с аргументом 0, будетработать для моего случая, потому что я всегда буду вызывать конструктор Data с 0.Однако я не могу изменить DemoClass, потому что он находится во внешней библиотеке.

Одно из решений, которое я рассмотрел, заключается в создании следующего класса:

class CustomData : public Data {
public:
    CustomData() : Data(0){}
};

Это работает, но кажется, чтонемного сложно.Есть ли более простой способ инициализации этого массива?

Ответы [ 3 ]

3 голосов
/ 03 июля 2019

Я нашел ответ на вашу проблему здесь .Итак, в вашем случае это решение должно применяться следующим образом:

#include <utility>
#include <array>

#define CONSTANT 2

class Data {
public:
    Data(int number){}
};

template<typename T, size_t...Ix, typename... Args>
std::array<T, sizeof...(Ix)> repeat(std::index_sequence<Ix...>, Args &&... args) {
   return {{((void)Ix, T(args...))...}};
}

template<typename T, size_t N>
class initialized_array: public std::array<T, N> {
public:
    template<typename... Args>
    initialized_array(Args &&... args)
        : std::array<T, N>(repeat<T>(std::make_index_sequence<N>(), std::forward<Args>(args)...)) {}
};

class DemoClass {
private:
    initialized_array<Data, CONSTANT> _member;
public:
    DemoClass():
        _member(1234)
    {
        // stuff
    }
};

Тогда ваш _member статически размещается массив фиксированного размера.Этот подход немного сложен, поэтому, возможно, кто-то может предложить более чистое решение.

2 голосов
/ 03 июля 2019

Простым решением является использование std::vector.Это, очевидно, имеет обратную сторону от введения динамического выделения:

std::vector<Data> _member;

DemoClass() : _member(CONSTANT, Data(0))

Это - это , можно сделать то же самое без динамического выделения, если вы используете элемент хранения необработанных символов (с достаточным размероми выравнивание), и создайте элементы с помощью размещения-нового.Вот что делает вектор.Это немного сложно, однако:

class DemoClass {
private:
    std::aligned_storage_t<sizeof(Data), alignof(Data)> storage[CONSTANT];
public:
    DemoClass()
    {
        std::uninitialized_fill(begin(), end(), Data(0));
    }

    Data* begin() {
        return std::launder(reinterpret_cast<Data*>(std::begin(storage)));
    }

    Data* end() {
        return std::launder(reinterpret_cast<Data*>(std::end(storage)));
    }

    ~DemoClass() {
        for(Data& d : *this)
            d.~Data();
    }
};
1 голос
/ 03 июля 2019

То, как вы написали пример кода, не так просто, потому что вы создали 2 конструктора для Data.

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

class Data {
public:
    Data(int number = 0){}
};

Теперь вы можете просто инициализировать скобкой с помощью пустой скобки:

_member{ }

Это обеспечит инициализацию всех членов.

Дополнительно я бы рекомендовал использовать std::array<Data, CONSTANT> вместо массива c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...