Calling std::make_tuple with lambdas in a variadic template - is this supposed to work in C++11?

梦想与她 提交于 2019-12-10 10:10:10

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!