storing / accessing variadic template arguments passed via constructor

Say i have a class like this:

1
2
3
4
5
6
7
8
9
class Parser
{
public:
template<class... Args>
Parser(Args&... args){}
virtual ~Parser() = default;

void Parse(){}
}


i want to be able to use & initialize the Parser class, passing in any list of arguments, like so
1
2
3
4
int a, b, c, d;

Parser parser{a,b,c,d}
parser.Parse();


then i want to be able to iterate over the list of passed arguments in the 'Parse' function. How can i achieve this in the most efficient way possible? Also while keeping the simplistic syntax of only having to write a comma separated list of variables? So by that i mean i'd rather not change the constructor to take a std::vector argument, because then i'd need to initialize it by writing Parser parser{std::vector<int>{a,b,c,d}}

also i think that would mean the vector would store pointers to each variable which seems unnecessary as all i really want is to reference them in the Parse function. Essentially nothing should need to be initialized or copied.

Also as a side question is there a way to ensure the Arguments passed in the variadic template are of a specific type? in this example only ints should be allowed
Use a non-template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <initializer_list>
#include <vector>
#include <iostream>

struct parser 
{ 
  std::vector<int> xs; 
  explicit parser(std::initializer_list<int> xs): xs(xs) {}
};

int main()
{
  parser p { 1, 2, 3, 4, 5 };
  
  for (int x : p.xs) std::cout << x << '\n';
}


You can think of std::initializer_list<T> as an array of T const with extra compiler support.

Also as a side question is there a way to ensure the Arguments passed in the variadic template are of a specific type? in this example only ints should be allowed

Yes, but it is unlikely to be worthwhile. That's mostly because it requires a commitment of effort to write and maintain code that doesn't actually make the user's computer do anything. It's also unlikely to prevent mistakes that the compiler wouldn't catch without help.

That doesn't mean the question is not worth exploring as a learning opportunity. Here are some implementations:

C++20:
1
2
3
4
5
6
7
struct parser
{ 
  template <typename... Ts> 
    explicit constexpr parser(Ts...) 
      requires (std::same_as<int, Ts> && ...) 
    {};
};

C++17:
1
2
3
4
5
6
7
struct parser
{
  template <typename... Ts, 
      typename = std::enable_if_t<(std::is_same_v<int, Ts> && ...)>>
    explicit constexpr parser(Ts...)
    {}
};

C++11:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace detail 
{ 
  template <typename...> struct all_int; 
  template <typename T, typename... Ts> 
    struct all_int<T, Ts...>
    {
      static constexpr bool test() 
      { return std::is_same<int, T>::value && all_int<Ts...>::test(); }  
    };
  template <> struct all_int<> { static constexpr bool test() { return true; } };
}

template <typename... Ts> 
  constexpr bool all_int() { return detail::all_int<Ts...>::test(); }

struct parser
{
  template <typename... Ts, 
      typename = typename std::enable_if<all_int<Ts...>()>::type>
    explicit constexpr parser(Ts...) 
    {}
};
Last edited on
Topic archived. No new replies allowed.