I have a working function template that calls a lambda.
I would like to generalize this function template to take variadic arguments and forward them perfectly into the lambda, but I am having trouble getting this code to compile.
I am using gcc 4.7.2.
UPDATE
Using R. Martinho Fernandes's suggestion, I looked up the error in bugzilla - it does look like a bug that's been around for a while. If anyone knows of a workaround (I'm digging around for one now), please post an answer - ty.
ERRORS
junk.cpp: In lambda function:
junk.cpp:32:68: error: parameter packs not expanded with ‘...’:
junk.cpp:32:68: note: ‘args’
junk.cpp: In instantiation of ‘std::pair<std::basic_string<char>, typename T::Lambda> MP(const string&, M, Args&& ...) [with T = Integer; M = int (Integer::*)()const; Args = {}; typename T::Lambda = std::function<std::function<int()>(const Integer&)>; std::string = std::basic_string<char>]’:
junk.cpp:47:42: required from here
junk.cpp:34:2: error: using invalid field ‘MP(const string&, M, Args&& ...)::<lambda(const T&)>::__args’
make: *** [junk] Error 1
CODE
#include <functional>
#include <iostream>
#include <map>
struct Integer
{
typedef std::function<int()> Function;
typedef std::function<Function( Integer const& inst )> Lambda;
virtual int getInt() const = 0;
};
struct IntImpl : public Integer
{
virtual int getInt() const { return 42; }
};
typedef std::function<int()> IntFunction;
typedef std::function<IntFunction( Integer const& inst )> IntLambda;
#define WONT_COMPILE
template<typename T,typename M,typename... Args>
std::pair<std::string,typename T::Lambda>
MP( std::string const& str, M method, Args&&... args )
{
#ifdef WONT_COMPILE
return std::make_pair( str,
[=]( T const& inst )
{
// COMPILE ERROR (Line 32) on next line
return std::bind( method, std::cref( inst ), std::forward<Args>(args)...);
}
);
#else
return std::make_pair( str,
[method]( T const& inst )
{
return std::bind( method, std::cref( inst ));
}
);
#endif
}
std::map<std::string,IntLambda> const g_intTbl =
{
MP<Integer>( "getInt", &Integer::getInt )
};
int
main( int argv, char* argc[] )
{
IntImpl x;
std::cerr << g_intTbl.find("getInt")->second( x )() << std::endl;
}
If anyone knows of a workaround (I'm digging around for one now), please post an answer
I ran into the exact same problem and found a workaround. It's kind of a late answer, hope you found a solution in the meantime, but here it is anyway (it could at the very least be useful to others).
The idea is to change the lambda's parameters so that it also accepts the same variadic arguments as the outer function (eg. [](int) {}
becomes [](int, Args&&... args) {}
) and bind
the lambda to the outer function's variadic arguments. Once this is done, no more problem forwarding the variadic arguments inside the lambda.
To sum it up:
template<typename... Args>
std::function<void (int)> foo(Args&&... args) {
return [&](int bar) {
// COMPILER BUG: doesn't work with GCC 4.7 despite the capture
doSomething(bar, std::forward<Args>(args)...);
};
}
template<typename... Args>
std::function<void (int)> foo(Args&&... args) {
return std::bind([](int bar, Args&&... args) {
// now this works with GCC 4.7
doSomething(bar, std::forward<Args>(args)...);
},
std::placeholders::_1, std::forward<Args>(args)...);
}
Sure this is an ugly hack, but at least you can still get the intended functionality even when you're stuck with a buggy compiler.
This appears to be a fault of the compiler (please report it if it isn't already). The standard says:
A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list (described below). The form of the pattern depends on the context in which the expansion occurs. Pack expansions can occur in the following contexts:
— [...]
— In a capture-list (5.1.2); the pattern is a capture.
— [...]
This makes your code correct.
Until you get a compiler that can handle this, you will have capture everything as a workaround, with [=]
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With