Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the order for variadic template pack expansion defined in the standard?

I thought that expanding a parameter pack had the following behavior:

// for Args ... p
f(p)...;
// was equivalent to
f(p1); f(p2); ...; f(pn);

But I just found out that gcc (4.6, 4.7 and 4.8) does it the other way around:

f(pn); ...; f(p2); f(p1);

Whereas clang does it as I expected.

Is that a bug in GCC or are they both valid according to the standard?

Minimal example

#include <iostream>
#include <string>

template<typename T>
bool print(const unsigned index, const T& value){
  std::cerr << "Parameter " << index << " is " << value << std::endl; 
  return true;
}

template<typename ... Args>
void printAll(Args ... args){
  unsigned i = 0;
  [](...){}(print(i++, args)...);
}

int main(){
  int a = 1; float b = 3.14; std::string c("hello");
  printAll(a, b, c);
}

Compiling and executing:

$> clang++ -std=c++11 -o test test.cpp
$> ./test
Parameter 0 is 1
Parameter 1 is 3.14
Parameter 2 is hello
$> g++ -std=c++11 -o test test.cpp
$> ./test
Parameter 0 is hello
Parameter 1 is 3.14
Parameter 2 is 1

Answer

It didn't take long for Martinho Fernandes to spot the error here. The problem is the order of evaluation of parameters, which is not defined by the standard (1.9.3):

Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified (for example, order of evaluation of arguments to a function).

like image 519
Thibaut Avatar asked Mar 07 '14 11:03

Thibaut


People also ask

How do you expand a parameter pack?

Parameter packs can only be expanded in a strictly-defined list of contexts, and operator , is not one of them. In other words, it's not possible to use pack expansion to generate an expression consisting of a series of subexpressions delimited by operator , .

What is Variadic template in C++?

Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.

What is a parameter pack?

[edit] A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates). A function parameter pack is a function parameter that accepts zero or more function arguments. A template with at least one parameter pack is called a variadic template.

What is pack expansion in variadic templates?

The variadic template feature also introduces pack expansion to indicate that a parameter pack is expanded. Two existing techniques, template argument deduction and partial specialization , can also apply to templates that have parameter packs in their parameter lists. A parameter pack can be a type of parameter for templates.

What is a variadic template pack in Python?

The pack can have arbitarary number of parameters having different types. At the end of compile-time, a variadic template function is translated into multiple solid functions that each of them accepts a certain number of parameters with certain types. There is no explicit for-loop commands to iterate the pack parameters.

What is the parameter pack expansion pattern?

Parameter pack expansion (appears in a body of a variadic template) pattern ... 5) Parameter pack expansion: expands to comma-separated list of zero or more pattern s. Pattern must include at least one parameter pack. A variadic class template can be instantiated with any number of template arguments: template<class ...

What are C++ Variadic templates and fold expressions?

What are C++ variadic templates and fold expressions? Introduced in C++11, a variadic template is a function or class template that accepts a parameter pack. The pack can have arbitarary number of parameters having different types.


1 Answers

Is the order for variadic template pack expansion defined in the standard?

Yes. The expanded elements are in an order that corresponds to the original order of the pack.

In the test, the expansion [](...){}(print(i++, args)...); is equivalent to: [](...){}(print(i++, a), print(i++, b), print(i++, c));.

The test is flawed in that it tests the order of evaluation of function arguments, which is a completely different matter. If you try and execute the expanded form presented above, you will observe the same behaviour. Or you would if the code didn't have undefined behaviour, since the variable i is incremented several times without the increments being sequenced.

like image 143
R. Martinho Fernandes Avatar answered Oct 18 '22 01:10

R. Martinho Fernandes