C ++ Инициализация члена массива с длиной, известной конструктору во время компиляции - PullRequest
0 голосов
/ 19 февраля 2019

Я пишу код для отображения на встроенном устройстве, которое отображает экраны, каждый с некоторым количеством кнопок.Количество кнопок отличается на разных экранах, но их количество известно во время компиляции.

Прямо сейчас, вот упрощенная версия того, как настроены классы:

class Button;

class Screen {
private:
    Button *buttons;
    unsigned int buttonCount;
public:
    Screen(Button *_buttons, unsigned int _buttonCount)
        : button(_buttons), buttonCount(_buttonCount) {}
};

И вот идея того, как я их использовал:

// For this example, Button has a constructor taking a string for
// the button's label
static Button buttonsForMainMenu[] = {
    Button("Do this"),
    Button("Do that"),
    Button("Exit")
};

Screen mainMenu (buttonsForMainMenu, 3);

Screen *currentScreen = &mainMenu;

int main() {
    // ...
    while (1) {
        currentScreen->show();
        // handle buttons, etc.
    }
}

Я бы хотел избежать отдельного массива для кнопок.В идеале, что-то вроде этого:

Screen mainMenu ({
    Button("Do this"),
    Button("Do that"),
    Button("Exit")
});

Этот код предназначен для встроенной системы, которая не нуждается в динамическом выделении памяти, поэтому я хотел бы избегать этого.std::array нужен размер массива при его объявлении в объявлении класса, но экраны будут иметь разное количество кнопок.Класс Screen, в который я не верю, может быть шаблонным из-за currentScreen.

Есть ли у класса способ иметь массив, в котором размер массива будет найден при его объявлении / во время компиляции?

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

Это можно сделать, используя C ++ 17's *1001* аргумент шаблона класса.Превратив Screen в шаблон, который принимает std::size_t в качестве параметра шаблона нетипичного типа.Затем, получив количество аргументов, переданных конструктору, вы можете использовать его в качестве значения параметра шаблона.Это позволит вам создать массив в Screen такого размера.Это означает, что каждый Screen, имеющий различное количество кнопок, имеет свой тип, но его можно наследовать от базового класса, если вам нужно хранить несколько Screen в однородном контейнере.

YouМожно видеть, что работа с этим минимальным примером

struct button
{
    std::string text;
};

template<std::size_t N>
struct screen
{
    button buttons[N];
    // constrain Args to only being buttons
    template<typename... Args, std::enable_if_t<std::is_same_v<std::common_type_t<Args...>, button>, bool> = true>
    screen(Args&&... args) : buttons{std::forward<Args>(args)...} {}
};

// get the number of arguments and use it for the array size
template<typename... Args>
screen(Args... args) -> screen<sizeof...(Args)>;

int main(){
    screen a{button{"one"}};
    screen b{button{"one"}, button{"two"}};
    screen c{button{"one"}, button{"two"}, button{"three"}};
}

Следующий код демонстрирует, как использовать базовый класс и виртуальные функции, чтобы вы могли использовать указатель на базовый класс для взаимодействия с различными screenс.

struct button
{
    std::string text;
};

struct screen_interface 
{ 
    void virtual show() = 0; 
};

template<std::size_t N>
struct screen : screen_interface
{
    button buttons[N];
    // contstrain Args to only being buttons
    template<typename... Args, std::enable_if_t<std::is_same_v<std::common_type_t<Args...>, button>, bool> = true>
    screen(Args&&... args) : buttons{std::forward<Args>(args)...} {}
    void show()
    {
        for (auto const& e : buttons)
            std::cout << e.text << "\n";
    }
};

// get the number of arguments and use it for the array size
template<typename... Args>
screen(Args... args) -> screen<sizeof...(Args)>;

int main(){
    screen a{button{"one"}};
    screen b{button{"one"}, button{"two"}};
    screen c{button{"one"}, button{"two"}, button{"three"}};

    screen_interface* si = &b;
    si->show();
    si = &a;
    si->show();
}
0 голосов
/ 19 февраля 2019

Один из способов сделать это - с помощью конструктора шаблона взять массив по ссылке:

template<size_t size>
Screen(Button (&_buttons)[size]): buttons(_buttons), buttonCount(size) {}

И вы будете использовать его так:

static Button buttonsForMainMenu[] = {
    Button("Do this"),
    Button("Do that"),
    Button("Exit")
};

Screen mainMenu(buttonsForMainMenu);

Обратите внимание, потому что Screen просто имеет указатель на кнопки, которым массив должен жить как минимум на протяжении экрана.Из-за этого я не думаю, что есть способ иметь синтаксис типа Screen mainMenu({Button("Do this"), ...}); без динамического выделения, поскольку любой временный массив будет уничтожен после конструктора.

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