1

Based on the recipe found here, I wrote the following:

void printInt(int a) {std::cout << a << std::endl;}

template <typename... Args>
void f(const Args &... args) {
    auto temp = {(printInt(args), void(), 0)...};
    auto a = temp; temp = a;
}

Two questions:

  1. What does this syntax mean: {(printInt(args), void(), 0)...}?

  2. I added the line auto a = temp; temp = a; in order to not get a warning about the unused variable temp. Is there a better way?


After the explanations in the reply and the comments, the only question that remains is: why doesn't C++ allow for this:

template <typename... Args>
void f(const Args &... args) {
    printInt(args)...;
} 

This question was asked in the cited post, but did not receive any answer.

Community
  • 1
  • 1
AlwaysLearning
  • 7,257
  • 4
  • 33
  • 68

1 Answers1

3
  1. This syntax means, that temp will be std::initializer_list<int> filled with zeros, due comma operator rules (all expressions will be evaluated, but only last will be used, so, function printInt will be called for each argument and 0 will be putted to initializer list for each argument).

N4296 5.19/1

A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded- value expression (Clause 5). 87 Every value computation and side effect associated with the left expression is sequenced before every value computation and side effect associated with the right expression. The type and value of the result are the type and value of the right operand; the result is of the same value category as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field. If the value of the right operand is a temporary (12.2), the result is that temporary.

{(printInt(args), void(), 0)...};

() is for pack expansion, so, ... will be applied to full expression, void() is here to shield the construct against classes overloading operator , (thanks Quentin), you can also use following

{((void)printInt(args), 0)...};

Also, it will be better to use

{0, ((void)printInt(args), 0)...};

for at least one element in initializer_list (so you can call f without arguments).

  1. You can use (void)temp; to mark variable unused, here is more info: What does (void) 'variable name' do at the beginning of a C function?
Community
  • 1
  • 1
ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • This answer is too laconic. I did not understand what exactly happens for 1: what is the role of the round parentheses and of `void()`? Also, for 2, why doesn't the compiler complain about the statement having no effect for `(void)temp;`? – AlwaysLearning Jul 31 '15 at 10:17
  • 2
    `void()` is here to shield the construct against classes overloading `operator ,`. – Quentin Jul 31 '15 at 10:23
  • @MeirGoldenberg and the compiler doesn't complain about `(void) temp;` because that's the conventional way to suppress unused variable warnings. – T.C. Jul 31 '15 at 10:25
  • @Quentin yes, thanks, but there is no need of it here. – ForEveR Jul 31 '15 at 10:26
  • 1
    @ForEveR To be pedantic, there might ;) `printInt` is called unqualified, and you have no guarantee that `Args` are all `int`s, so you might pick up something weird via ADL. :P (The fix to that problem is probably suppressing ADL via a qualified call, though.) – T.C. Jul 31 '15 at 10:29
  • 1
    @MeirGoldenberg of course ! It is possible for classes to overload `operator ,`, the comma operator. Let's say `printInt(arg)` returns `Foo`, then if an overloaded `Bar operator , (Foo, int)` exists, it will be called by `(printInt(args), 0)`. If `Bar` is not always the same type for every possible `Foo`, you end up trying to stuff objects of different types into the same `std::initializer_list`, which is an error. – Quentin Jul 31 '15 at 10:52
  • 1
    @MeirGoldenberg Now, the solution : `void()` is not exactly an empty expression cast (there's no empty expression), but the result is the same : you obtain an expression of type `void`. And it's impossible to formulate an overloaded `operator , (Foo, void)` or `operator , (void, int)`. By inserting the `void` expression, you are thus certain than the built-in comma operator is the one getting used. – Quentin Jul 31 '15 at 10:54
  • Thank you! Too bad I deleted the comment that asked the question. The only remaining question is the one I added at the end of my post. – AlwaysLearning Jul 31 '15 at 11:07
  • @MeirGoldenberg look here http://stackoverflow.com/questions/25680461/variadic-template-pack-expansion and you can also read 14.5.3/4 par of standard, where contexts, where pack expansion can occur are listed. – ForEveR Jul 31 '15 at 11:26
  • @ForEveR I asked about the reasoning. Saying "this is not one of the cases listed in the standard" does not answer the question. The answer might be of the form "if this was allowed, it would result in such and such problems" or "there is no good reason for this; hopefully it will be allowed by a future standard". – AlwaysLearning Jul 31 '15 at 12:09