问题
in C++11 a lambda function is an object, and it should be possible to call make_tuple with it, right?
void foobar() {
auto t = std::make_tuple([](){ std::make_shared<int>(); });
}
This code works for me.
Now what happens if we add a variadic template:
#include <tuple>
#include <memory>
template <class... T>
void foobar() {
auto t = std::make_tuple([](){ std::make_shared<T>(); }...);
}
int main(int, char**)
{
foobar<int, float, double>();
return 0;
}
This one fails to compile in GCC 4.7.2
main.cpp: In lambda function:
main.cpp:6:54: error: parameter packs not expanded with '...':
main.cpp:6:54: note: 'T'
main.cpp: In function 'void foobar()':
main.cpp:6:57: error: expansion pattern '#'lambda_expr' not supported by dump_expr#<expression error>' contains no argument packs
main.cpp: In instantiation of 'void foobar() [with T = {int, float, double}]':
main.cpp:11:29: required from here
I wonder, is this code correct by the Standard ?
回答1:
This is a known bug in gcc: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41933 (the initial example is a different pattern covering capture of parameter packs, but the issue underlying that and the merged bugs is that gcc simply hasn't implemented the intersection of lambdas and variadics).
As far as the standard is concerned it's perfectly acceptable code.
回答2:
Approach #1:
The simples way to work around it seems std::function
:
#include <tuple>
#include <memory>
template <class T>
std::function<std::shared_ptr<T>()> make_shared_lambda() {
return [](){ return std::make_shared<T>(); };
}
template <class... T>
void foobar() {
auto factories = std::make_tuple(make_shared_lambda<T>()...);
auto tracer = std::get<2>(factories)();
}
// demonstration
#include <cstdio>
struct Tracer {
Tracer() { puts("Tracer()"); }
~Tracer() { puts("~Tracer()"); }
};
int main()
{
foobar<int, float, Tracer>();
}
Prints
Tracer()
~Tracer()
Approach #2:
Obviously there is a performance overhead in std::function
's type erasure. You may handily exploit the fact that stateless lambdas are convertible to function pointers (standard 5.1.2/6):
#include <tuple>
#include <memory>
template <class T> auto make_shared_f() -> std::shared_ptr<T>(*)()
{
return []() { return std::make_shared<T>(); };
}
template <class... T> std::tuple<std::shared_ptr<T>(*)()...> foobar() {
return std::make_tuple(make_shared_f<T>()...);
}
// demonstration
int main()
{
auto factories = foobar<int, float, double>();
auto double_ptr = std::get<2>(factories)();
}
If your compiler supports template aliases, you can make it a tiny little bit less cryptic:
template <typename T> using shared_factory = std::shared_ptr<T>(*)();
template <class T> shared_factory<T> make_shared_f() {
return []() { return std::make_shared<T>(); };
}
来源:https://stackoverflow.com/questions/13469851/calling-stdmake-tuple-with-lambdas-in-a-variadic-template-is-this-supposed-t