c++templateslambdac++14

What's the cleanest way to pass a typename into a lambda?


I'm interested in writing a lambda function that will, among other things, call std::make_unique. To call std::make_unique I need a typename, but it would seem that in order to directly pass the typename into the lambda function, I would have to make the lambda variable a template:

struct SpecialThing
{
    SpecialThing(/* some arguments */) {}
}

void f()
{
    template <typename Thing>
    auto createThing = [](auto&&... parameters)
    {
        return std::make_unique<Thing>(std::forward<decltype(parameters)>(parameters)...);
    };

    auto thing = createThing<SpecialThing>(/* some construction parameters */);
}

But my compiler (GCC 4.9.2) doesn't seem to like this. (I didn't really expect it to, though I know little enough about template variables that I couldn't be sure it wouldn't.)

Suppose I'm really determined to keep createThing a local variable of lambda-function type. How cleanly can I manage to wrap std::make_unique in this fashion? Here's the best I've got so far:

void f()
{
    auto createThing = [](auto dummyThingPointer, auto&&... parameters)
    {
        typedef typename std::remove_pointer<decltype(dummyThingPointer)>::type Thing;
        return std::make_unique<Thing>(std::forward<decltype(parameters)>(parameters)...);
    };

    auto thing = createThing(static_cast<SpecialThing*>(nullptr), /* some construction parameters */);
}

It's wordy, but it isn't terribly hard to understand, and it compiles.

I think maybe I could do something similar with std::declval and std::remove_reference but I couldn't get that to compile. Anyway it wouldn't be much cleaner than the above.

Does C++14 provide any non-sneaky way to get the SpecialThing type into createThing here? Or failing that, a sneaky way that's better than my nullptr trick?

(Note: I know I could work around this in other ways; I'm just asking to learn about the language, not to get past a serious obstacle. Hence the silly code above that trivially wraps a standard function for no apparent reason.)


Solution

  • I would use a tag type:

    #include <memory>
    struct SpecialThing
    {
        SpecialThing(/* some arguments */) {}
    };
    template<typename T>
    struct Tag {
        typedef T type;
    };
    void f()
    {
        auto createThing = [](auto typeTag, auto&&... parameters)
        {
            return std::make_unique<typename decltype(typeTag)::type>(
                            std::forward<decltype(parameters)>(parameters)...);
        };
    
        auto thing = createThing(Tag<SpecialThing>{} /*, some construction parameters */);
    }