我正在为显示屏幕的嵌入式设备上编写显示代码,每个屏幕都有一些按钮.屏幕之间的按钮数量不同,但数量在编译时是已知的.
现在,这是一个如何设置类的简化版本:
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.
有没有办法让类有一个数组,在其声明/编译时找到数组大小?
这可以通过利用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)