C++在编译时使用构造函数已知的长度初始化数组成员

cly*_*yne 1 c++ c++17

我正在为显示屏幕的嵌入式设备上编写显示代码,每个屏幕都有一些按钮.屏幕之间的按钮数量不同,但数量在编译时是已知的.

现在,这是一个如何设置类的简化版本:

class Button;

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

以下是我如何使用它们的想法:

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

我想避免为按钮设置单独的数组.理想情况下,这样的事情:

Screen mainMenu ({
    Button("Do this"),
    Button("Do that"),
    Button("Exit")
});
Run Code Online (Sandbox Code Playgroud)

此代码适用于不需要动态内存分配的嵌入式系统,因此我想继续避免这种情况.std::array需要在类声明中声明的数组大小,但屏幕将具有不同数量的按钮.我不相信的Screen类可以因为它而被模板化currentScreen.

有没有办法让类有一个数组,在其声明/编译时找到数组大小?

Nat*_*ica 6

这可以通过利用C++ 17的类模板参数推导来完成.通过转换Screen为一个模板,该模板采用std::size_t非类型模板参数.然后,通过获取传递给构造函数的参数数量,可以将其用作模板参数的值.然后,这允许您创建一个Screen具有该大小的数组.这意味着每个Screen拥有不同数量的按钮的按钮类型不同,但如果需要Screen在同类容器中存储多个s,则可以从基类继承它.

你可以看到使用这个最小的例子

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

以下代码演示了如何使用基类和虚函数,以便您可以使用指向基类的指针与不同的screens 进行交互.

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