3

In the code below I expand a parameter pack inside an initializer list, while calling a function DoSomethingReturnInt on each element. Below that I attempt to do something seemingly similar to try and call DoSomething on each element, but get a compiler error. Is this simply not possible or do I simply have to modify it slightly to accomplish this? It seems to me that something like this should be possible.

template <class T>
int DoSomethingReturnInt(T&& t) 
{}

template <class T>
void DoSomething(T&& t) 
{}

template <class... T>
void variadic(T&&... args) 
{
    int arr[] = { DoSomethingReturnInt(args)... }; //Compiles OK
    DoSomething(args)...; //error: parameter packs not expanded with '...'
}

int main()
{
    variadic("Testing", "one", 2.0, 3);
}
Constructor
  • 7,273
  • 2
  • 24
  • 66
Chris_F
  • 4,991
  • 5
  • 33
  • 63
  • Typically you use recursion to go through the arguments, see my [example here](http://stackoverflow.com/a/16338804/1708801). – Shafik Yaghmour Jul 05 '14 at 01:26
  • @ShafikYaghmour I'm aware that this can be done with recursion and overloading. I thought perhaps there was a simpler way considering how clean the syntax is for expanding `DoSomethingReturnInt(args)...` inside of an initializer list. Am I expecting too much? ;) – Chris_F Jul 05 '14 at 01:28
  • Pack expansion is only allowed in certain contexts; a freestanding statement is not one of them. – T.C. Jul 05 '14 at 01:33

2 Answers2

4

This is not a valid location for parameter pack expansion. The valid contexts for pack expansion is covered in the draft C++ standard section 14.5.3 Variadic templates which says:

A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list (described below). The form of the pattern depends on the context in which the expansion occurs. Pack expansions can occur in the following contexts:

— In a function parameter pack (8.3.5); the pattern is the parameter-declaration without the ellipsis.

— In a template parameter pack that is a pack expansion (14.1):

— if the template parameter pack is a parameter-declaration; the pattern is the parameter-declaration without the ellipsis;

— if the template parameter pack is a type-parameter with a template-parameter-list; the pattern is the corresponding type-parameter without the ellipsis.

— In an initializer-list (8.5); the pattern is an initializer-clause.

— In a base-specifier-list (Clause 10); the pattern is a base-specifier.

— In a mem-initializer-list (12.6.2); the pattern is a mem-initializer.

— In a template-argument-list (14.3); the pattern is a template-argument.

— In a dynamic-exception-specification (15.4); the pattern is a type-id.

— In an attribute-list (7.6.1); the pattern is an attribute.

— In an alignment-specifier (7.6.2); the pattern is the alignment-specifier without the ellipsis.

— In a capture-list (5.1.2); the pattern is a capture.

— In a sizeof... expression (5.3.3); the pattern is an identifier.

This is also covered on cppreference section for Parameter pack.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
4

You can use the comma operator to your advantage here.

int dummy[] = { (DoSomething(args), 0)... };

EDIT: If you don't like the comma operator "abuse", maybe a lambda?

int dummy[] = { []() { DoSomething(args); return 0; }()... };

Note that gcc4.9 doesn't seem to be able to handle this, but clang will do just fine.

mpark
  • 7,574
  • 2
  • 16
  • 18
  • I can't quite decide if this is good idea or not. :) – Chris_F Jul 05 '14 at 02:00
  • 1
    @Chris_F as I have argued [here](http://stackoverflow.com/a/18444099/1708801) there are not many good uses for the comma operator and a lot of ways to abuse it. This looks like an abuse to me. – Shafik Yaghmour Jul 05 '14 at 02:16