8

It would be nice if I could do the following:

template <class RT, class... PT>
class Event 
{
    ...
    void operator()(PT... args)
    {
        std::for_each(
            l.begin(), l.end(), [args...](Handler *p) { (*p)(args...); }
        );
    }
    ...
};

Unfortunately I couldn't make it compile with g++ 4.7.2 (-std=c++0x):

evtempl.hh: In member function 'void elt::Event::operator()(PT ...)': evtempl.hh:75:54: error: expected ',' before '...' token evtempl.hh:75:54: error: expected identifier before '...' token evtempl.hh:75:57: error: parameter packs not expanded with '...': evtempl.hh:75:57: note: 'args' evtempl.hh: In lambda function: evtempl.hh:76:26: error: expansion pattern 'args' contains no argument packs evtempl.hh: In instantiation of 'void elt::Event::operator()(PT ...) [with RT = void; PT = {int}]': testevtempl.cc:28:9: required from here evtempl.hh:74:9: error: using invalid field 'elt::Event::operator()(PT ...)::::Handler*)>::__args' evtempl.hh: In instantiation of 'void elt::Event::operator()(PT ...) [with RT = void; PT = {int, const char*}]': testevtempl.cc:29:20: required from here evtempl.hh:74:9: error: using invalid field 'elt::Event::operator()(PT ...)::::Handler*)>::__args'

instead, I have to change that lambda to the old, mundane syntax:

for (itr = l.begin(); itr != l.end(); ++itr)
     (*(*itr))(args...);

This one compiles and works fine.

I wonder why the lambda syntax doesn't work.

  1. Did I do something wrong or miss something?
  2. Is such thing outlawed in the c++11 standard?
  3. or, this is allowed by the standard, but it's a problem of the current compiler?

I tried

[=](Handler *p) { (*p)(args...); }

it gives the same error as if you did:

[args](Handler *p) { (*p)(args...); }

complaining parameter packs not expanded

Cœur
  • 37,241
  • 25
  • 195
  • 267
  • 3
    Have you tried `[=]` instead? – Kerrek SB Jan 16 '13 at 22:07
  • @Kerrek SB good suggestion, but it doesn't work either –  Jan 16 '13 at 22:10
  • 3
    If you isolate the lambda `auto foo = [=](Handler *p) { (*p)(args...); };`, you get two errors "1) Parameter pack not expanded and 2) expansion pattern 'args' contains no parameter packs". I suspect that it's a GCC bug, as it's complaing that args is both not a pack and must be one. – Dave S Jan 16 '13 at 22:18
  • Seems like a duplicate of http://stackoverflow.com/questions/14270941/can-a-parameter-pack-be-captured-implicitly-within-c11-lambdas – lethal-guitar Jan 16 '13 at 22:22
  • @lethal-guitar: That question is similar. However, this question uses explicit capture, while the linked question is talking about implicit capture. – Jesse Good Jan 16 '13 at 22:28
  • @JesseGood ah yes, you're right. – lethal-guitar Jan 16 '13 at 22:31
  • It works on clang, http://liveworkspace.org/code/pstVG$3 – oblitum Jan 17 '13 at 00:42

1 Answers1

7

It's a bug in gcc. See [c++0x]lambdas and variadic templates don't work together or perhaps [C++11] Pack expansion fails in lambda expressions.

Jesse Good
  • 50,901
  • 14
  • 124
  • 166
  • Indeed, it's been a bug for a very long time already and as far as I know it is still a bug in gcc 4.8 and clang. – gnzlbg Jan 16 '13 at 22:47
  • @gnzlbg, it's not a bug in clang. – oblitum Jan 17 '13 at 00:26
  • @chico Cool! Thanks! I didn't knew it worked in clang already! If only expanding `f(args)...;` to `f(arg1), f(arg2), ...;` would work... – gnzlbg Jan 17 '13 at 10:58
  • @gnzlbg there's this hack to expand a pack pattern merely for its side-effects: `std::initializer_list{ (void(f(args)), 0)... };`. You can put it in a macro or something. And note that `{}`-initialization guarantees order of evaluation. – R. Martinho Fernandes Feb 12 '13 at 05:08
  • `f(args), 0` executes f(args), and evaluates to zero (look up comma operator if you are not familiar). I added `void` around f as a precaution against potential overloaded comma operators, which we don't want to kick in here (because you cannot overload it with a void argument). So I just build an initializer list full of zeros, that happens to execute all those side-effects. The initializer list is immediately discarded. Should have no overhead. Worst case scenario your compiler stores a readonly static array of zeros in your executable, but I would expect compilers to realize it isn't used. – R. Martinho Fernandes Feb 12 '13 at 10:49