Search code examples
c++ooptemplatesvariadic-templatesgeneric-programming

Is it possible and/or good practice to use parameter packs as class attributes in C++?


I am coding a trivial template struct (in C++) that can display a formated "menu" in the console. I'm doing this mostly to play around with variadic arguments. The struct's template works by automatically deducing an input type (InputTy), a prompt type (PromptTy, probably usually either a std::string or const char*), and a parameter pack for the message (class... MsgTypes). The class's declaration looks like:

template<class InputTy, class PrompTy, class... MsgTypes>
struct Menu 
{
    // ... Stuff in here ... //
};

What I'd like to do with this class is save the MsgTypes as a sort of "variadic class attribute". Essentially, this would look like:

template<class InputTy, class PromptTy, class... MsgTypes>
struct Menu
{
public:
    Menu(PromptTy prompt_text, MsgTypes... menu_text) : prompt_text(prompt_text),
        menu_text(menu_text) {}

    // Displays the formatted menu (uses variadic attribute)
    inline void display() 
    {
        display_recurse(menu_text);
        std::cout << std::endl << prompt_text;
        std::cin >> input_var; std::cin.ignore(10000, '\n');
        /* ... Etc ... */
    }

protected:
    InputTy input_var;
    PromptTy prompt_text;
    MsgTypes... menu_text;  // Variadic attribute

    // Recursive function for displaying the text of a menu
    template<typename Ty, typename... Types>
    inline void display_recurse(Ty cur_line, Types next_lines)
    {
        std::cout << cur_line << std::endl;
        display_recurse(next_lines);
    }

    // No-arg function overload for recursive generic function with parameter pack
    inline void display_recurse() {}
};

Solution

  • Is it possible ... to use parameter packs as class attributes in C++?

    Not quite. Argument packs can only be expanded into argument lists, expressions or initialisers, but not into declarations.

    There is a standard template std::tuple, which achieves essentially what you might be trying to do. It is a template class with each variadic type argument as a member object. So, in your case you might have a member:

    std::tuple<MsgTypes...> menu_text;
    

    In case you're wondering, how does tuple itself achieve this: You can take one template argument as non-pack, have that as member and inherit another instance of the template recursively with the rest of the parameter pack.