Alex Alex - 4 months ago 18
C++ Question

Why do we have to use additional construction when iterating variadic template parameters?

Why do we have to use additional construction such as (

non()
- function,
temp[]
- array, or empty
[](...){}
- lambda) when iterating variadic template parameters?

As known, we can iterate a parameter pack with variadic templates in C++ by using some of these ways:

http://ideone.com/GXDPDw

#include <iostream>
#include <cstdlib>
#include <valarray>
#include <numeric>
using namespace std;

template<typename ...Args> constexpr inline void non(Args ...) {}

template<typename T, typename ...Args>
inline T sum1(T val, Args ...args) { non(val += args ...); return val; } // v1
// why do we need some function non() here?

template<typename T, typename ...Args>
inline T sum2(T val, Args ...args) { auto tmp = { val += args... }; return val; } // v2
// why do we need some array tmp[] here?

template<typename T, typename ...Args>
inline T sum3(T val, Args ...args) { [](...){}((val += args)... ); return val; } // v3
// why do we need empty lambda [](...){} here?

template<typename T, typename ...Args>
inline T sum4(T val, Args ...args) { for(auto &i:{ args... }) val += i; return val; }//v4

template<typename ...Args, typename T = common_type_t<Args...>>
inline T sum5(Args ...args) { return std::valarray<T>({ args... }).sum(); } // v5


template<typename T> constexpr inline T sum6(T val) { return val; }

template<typename T, typename ...Args>
constexpr inline T sum6(T val, Args ...args) { return val + sum6(args...); } // v6


int main() {
cout << sum1(1, 2, 3) << endl;
cout << sum2(1, 2, 3) << endl;
cout << sum3(1, 2, 3) << endl;
cout << sum4(1, 2, 3) << endl;
cout << sum5(1, 2, 3) << endl;
cout << sum6(1, 2, 3) << endl;

return 0;
}


But why do we need to use:


  1. non(val += args ...);
    instead of
    val += args...;

  2. auto tmp = { val += args... };
    instead of
    val += args...;

  3. [](...){}((val += args)... );
    instead of
    val += args...;



It would be much clearer and easier to use so:

template<typename T, typename ...Args>
inline T sum(T val, Args ...args) { val += args...; return val; }


Why is no there in the standard such possibility, or could such a possibility carry any danger?

And will be such possibility in C++17 or later?

Answer

This is because parameter packs must be expanded in a context which expects a syntactic list. A normal function scope is not such a context, so you can't just write val += args...;.

However, in C++17, we'll get fold expressions, which will allow you to rewrite your code like so:

template<typename T, typename ...Args>
inline T sum(T val, Args ...args) { (val += ... += args) ; return val; }

This will be expanded to (((val += arg0)) += arg1) += arg2) for three arguments.

Another option would be to write it like this:

val += (... + args);

This expands to val += ((arg0 + arg1) + arg2)