The comma operator is not the same as the comma expression separator.
The comma operator takes two expressions, evaluates the left hand side, discards it, evaluates the right hand side, and returns the result.
The expression separator is used when you have a list of expressions, like a function call or initializer list.
decltype(a,b,c)
is decltype(
expression )
, not decltype(
expression-list )
. Which means that the ,
in your decltype
is operator comma.
In general, ...
expansion only works when the grammar allows a list of expressions. The ,
"generated" is the expression separator, not the comma operator.
I am not aware of a way you can emulate the behavior of the ,
operator, including execution order, using ...
. If you don't care what order they are evaluated in, you can do:
template<class T, class... Ts>
struct last_type_helper{using type=T;};
template<class T0, class T1, class... Ts>
struct last_type_helper<T0, T1, Ts...>:last_type_helper<T1, Ts...>{}
template<class... Ts>
using last_type=typename last_type_helper<Ts...>::type;
template<class T0>
T0&& last_expression( T0&& t0 ) { return std::forward<T0>(t0); }
template<class T0, class...Ts>
auto last_expression( T0&& t0, Ts&&...ts )->last_type<T0, Ts...>&& {
return last_expression( std::forward<Ts>(ts)... );
}
then
template<class...Args>
auto g(Args&&...args) -> decltype(last_expression(args...));
works, as does
template<class...Args>
auto g(Args&&...args) -> last_type<Args...>;
which puts the cart after the horse, no?