5

I know that we can't use variadic expansions as if it is a chain of comma operators. In that question the sample is like this:

template<typename... Args>
inline void increment_all(Args&... args) 
{
    ++args...; 
}

It might be ambiguous either to increment or expand first so parentheses won't hurt:

template<typename... Args>
inline void increment_all(Args&... args)
{
    (++args)...; 
}

or something like this:

template<typename... Args>
void cout_all(Args&&... args)
{
    (std::cout << std::forward<Args>(args))...; 
}

I know that we can use some recursion tricks to get what we want, like this. What I don't know is why does not the standard describe such behavior? I mean, what is the reason behind it?

zahir
  • 1,298
  • 2
  • 15
  • 35
  • 3
    That's an interesting question. My suspicion is that either they didn't want to deal with it, or considered having a pack expand either to an argument list or an invocation of the `,` operator depending on context to be really ugly, especially if you overload the `,` operator. – Omnifarious Jan 20 '13 at 23:02
  • 3
    What was the reason for *not* adding your favourite pet feature to a language that's already very complex? You'll have to ask the standards committee. The point is that the entire notion of variadic templates was *added* to the existing standard, and so asking why any given *other* idea you might have wasn't also added is a bit moot. – Kerrek SB Jan 20 '13 at 23:02
  • Thank you for asking this question. While I think that allowing the expansion you suggest is a bad idea, thinking about this helped me come up with a much better mental model for how `...` worked. – Omnifarious Jan 25 '13 at 18:05

1 Answers1

5

The other contexts where a pack expansion is allowed are lists where a comma is a separator between list elements, not an operator.

For example, f(args...) expands to a function argument list, tuple<Args...> expands to a template argument list.

In your examples the pack expansion forms a statement, and commas between sub-expressions of a statement are the comma operator, which could be overloaded, leading to arbitrarily complicated code, and unlike the builtin comma operator, not forcing left-to-right evaluation. You'd be surprised if your (std::cout << std::forward<Args>(args))...; example wrote out the args in unspecified order because one of the types in the parameter pack overloaded operator<< and operator, and broke the order of evaluation.

Doing this would not be a simple extension to the current rules, it would be a completely different context with very different effects.

It might be ambiguous either to increment or expand first so parentheses won't hurt:

No, it wouldn't be ambiguous. It's OK to use f(++args...) and it's clear and unambiguous. The difficulty with your suggestion is not how to parse ++args... it's what happens after you expand it to a statement containing comma operators.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • 1
    +1 The key point is that the `,`-operator is totally different from the comma separating function and template arguments. In the end if you could expand them with `,`, why not also `+` or `<<`, they're all operators (though it would be a nice feature, I admit ;)). Maybe expanding as individual statements, thus using `;`, would have made much more sense (and IMHO doesn't come with as many problems). But ok, recursive template enthusiasts need something to do, too. – Christian Rau Jan 21 '13 at 08:53
  • What is problematic if it would invoke user defined comma operator overloads? I think many people have asked for this extensio, and IMO C++ surely should provide it. `(std::cout << std::forward(args))...` would never write out the args in unspecified order - it would always generate the same expression. It's just that the interpretation of the expression depends on the context, but this would be true if the programmer would write the comma operator elements explicitly aswell. – Johannes Schaub - litb Apr 15 '14 at 17:16