3

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 ?

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
Gart
  • 2,297
  • 1
  • 28
  • 36

2 Answers2

6

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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
4

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>(); };
}
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    Your approach #2 seems to be wrong. OP's `foobar()` constructs a "tuple of nullary functions each returning `shared_ptr`", but yours construct a "nullary function returning a tuple of default-constructed `shared_ptr` (?)". – kennytm Nov 20 '12 at 12:29
  • @KennyTM you appear to be right. Let me rewrite that second one :) – sehe Nov 20 '12 at 13:29
  • I have added a better workaround without using `std::function`. – sehe Nov 20 '12 at 13:31