我正在为嵌入式设备上的显示器编写代码,该设备可显示屏幕,每个屏幕都有一些按钮。屏幕之间的按钮数量不同,但是在编译时已知该数量。
现在,这是有关如何设置类的简化版本:
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
需要在类声明中的声明位置处具有数组大小,但是屏幕将具有不同数量的Button。我不相信可以使用currentScreen
来创建Screen类的模板。
类是否可以在声明时/在编译时找到数组大小的数组?
答案 0 :(得分:6)
这可以通过利用C ++ 17的Class template argument deduction来完成。通过将Screen
变成一个以std::size_t
作为非类型模板参数的模板。然后,通过获取传递给构造函数的参数数量,可以将其用作模板参数的值。然后,您可以在Screen
中创建一个具有该大小的数组。这意味着每个具有不同按钮数量的Screen
都是不同的类型,但是如果您需要在同质容器中存储多个Screen
,则可以从基类继承它。
您可以看到使用此最小示例
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();
}
答案 1 :(得分:2)
一种方法是使用模板构造函数通过引用获取数组:
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"), ...});
这样的语法的方法,因为在构造函数之后,任何临时数组都将被破坏。