2

I'm trying to implement a delegate class following Herb Sutter's Example. There is a sections in this article that duplicates several templates; one template for the number of arguments in the list (Example 7, lines 41 - 59)1. I'm trying to replace this with a variadic template.

void operator()() const {
    for_each(begin(l_), end(l_), []( function<F> i) {
            i(); 
    });
}

template<typename... Ts>
    void operator()(Ts... vs) const {
        for_each(begin(l_), end(l_), [&, vs...]( function<F> i) //g++-4.6.1: expected ',' before '...' token; expected identifier before '...' token
        { 
                i(vs...);
        });
    }

I found this answer, but I think my issue is the vs isn't expanding. What is the correct way to do this?

Community
  • 1
  • 1
Jeremy W
  • 23
  • 3
  • This is not the proper idiom for forwarding functions. What you want is to take your parameters as `(Ts&& ...vs)` and then forward them to the function as `i(std::forward(vs)...)`. That probably won't solve your problem, but this is how perfect forwarding is done in C++11. – Nicol Bolas Mar 25 '12 at 01:36
  • I may not understand your point, but I don't want to move the arguments. A copy is the correct thing in this case. Each callback gets a copy of the event arguments. There may be a case where move-semantics will work, but copy is more general. Right? – Jeremy W Mar 25 '12 at 05:37
  • Forwarding will move or copy the arguments as is appropriate. The idea behind perfect forwarding is that it is *perfect*: identical to how you would call `i` directly. So if `i` takes its arguments by value and you pass an lvalue, it will copy. If you pass an rvalue, it will move, which is *exactly* what would happen if you called `i` directly. If the user wants to move into a function parameter, you can't (and shouldn't) stop them. That's their business. All you need to worry about is perfectly forwarding the arguments, and that's the C++11 syntax for doing so. – Nicol Bolas Mar 25 '12 at 05:59

2 Answers2

2

I'm not sure why vs isn't expanding but maybe the easy work-around is to pass arguments by value as default and name the known objects needing pass by reference. This way the expansion in the capture-list isn't needed. Personally, I wouldn't be surprised if this were a bug in gcc (your example is incomplete - otherwise I'd try it with a recent SVN versions of gcc and clang).

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
1

This seems to be an old gcc bug that still persists. Might want to give the maintainers a friendly nudge.

A workaround could be:

#include <vector>
#include <algorithm>
#include <functional>
#include <tuple>

using namespace std;

template<typename F>
struct Foo {
  template<typename... Ts>
  void operator()(Ts... vs) const {
    auto t = tie(vs...);

    for_each(begin(l_), end(l_), 
             [&t]( function<F> i) 
             { 
               // figure out the function arity and unpack the tuple
               // onto it. This is could be more difficult than one
               // thinks.

               // i(vs...);
             }
      );
  }
  vector< function< F > > l_;
};
pmr
  • 58,701
  • 10
  • 113
  • 156
  • Thank you I removed the lambda: `template void operator()(Ts... vs) const { for(auto i = begin(l_); i != end(l_); ++i) { (*i)(vs...); } }` – Jeremy W Mar 25 '12 at 04:10
  • You can still improve that by using `auto i : l_` :) – pmr Mar 25 '12 at 12:33