19

I already know the stdarg.h way to have a function with variable arguments in c++ as discussed here for example. I also know c++11 standard has variadic templates as explained here.

But in both of aforementioned schemes we don't know (and we can't force) argument types in compile time afaik. What I'm looking for is to pass variable arguments of known types to a function. I think this can be done because I read about it here:

Variadic templates, which can also be used to create functions that take variable number of arguments, are often the better choice because they do not impose restrictions on the types of the arguments, do not perform integral and floating-point promotions, and are type safe.

Is it possible? If yes, how can I do this?

Constructor
  • 7,273
  • 2
  • 24
  • 66
melmi
  • 988
  • 3
  • 9
  • 27

2 Answers2

30

It is straight forward to write a function with variadic templates, that accept an arbitrary number of arguments. The only difference to the general pattern is, that a concrete type is used as first argument (head) - instead of a template parameter. The following example shows a function foobar, that accepts an arbitrary number of strings.

// used for end of recursion - and for the empty arguments list
void foobar() { }

template <typename ...Tail>
void foobar(const std::string& head, Tail&&... tail)
{
    // do something with head
    std::cout << head << '\n';
    // call foobar recursively with remaining arguments
    foobar(std::forward<Tail>(tail)...);
}

foobar("Hello", "World", "...");

Personally, I prefer using std::initializer_list instead of variadic templates. Because variadic templates are more complex and require additional experience. With std::initializer_list, it might look like this:

void foobar(std::initializer_list<std::string> values)
{
    for (auto& value : values) {
        // do something with value
        std::cout << value << '\n';
    }
}

foobar({ "Hello", "World", "...", });

Unfortunately, the additional curly braces are required when using std::initializer_list with regular functions. They are not required for constructors, if the new initializer syntax is used.

Edit: Rewrote the answer according to the feedback. In particular I have changed the order of the two solutions/examples.

nosid
  • 48,932
  • 13
  • 112
  • 139
  • 1
    I think it was all about my misunderstanding. I knew the second way, but I always thought there is no control on type of parameters. This is not true. because the function takes a first parameter of a known type (string here), it is implicitly forced to have its parameters of that type. Thank you. – melmi Apr 06 '12 at 16:39
  • 2
    You may want to move the second code-box first. It's a very good solution to the problem, while the `initializer_list` version isn't. I was going to post a big, complex thing with SFINAE and such, but this is far more reasonable. – Nicol Bolas Apr 06 '12 at 16:43
  • 1
    @nosid you may actually avoid using curly braces if you mix both the std::initializer_list and the variadic template approach, by creating a variadic template wrapper around the std::initializer_list version, like this: [see on coliru](http://coliru.stacked-crooked.com/a/4baef67192a0310c). – Fabio A. May 31 '16 at 02:37
  • @FabioA. You can do that, but you lose an important benefit of `std::initializer_list`. It is no longer possible to use _brace initialisation_ for the element, i.e. the following works with `std::initializer_list`: `foobar({{"foo", 1, 2}, {"bar", 2, 1}});`. – nosid May 31 '16 at 20:49
  • @nosid Indeed, and it would still work even when the wrapper is available. To use those string constructors with the template wrapper you would have to explicitely name the std::string type. It's illustrated in [this other coliru](http://coliru.stacked-crooked.com/a/fa752e8c38bf546e). – Fabio A. Jun 01 '16 at 07:58
2

If variable parameters are all of one type, you can change the function signature to take an array of those types instead of using the '...'.

Colin D
  • 5,641
  • 1
  • 23
  • 35
  • An array of one type needs iteration, while using of variadic functions provides a compile time inline mechanism. You may say that looping on an array is not so time conuming, but consider it in another multiple large loops. – melmi Apr 06 '12 at 14:10