Ihor Stuzhuk
Ihor Stuzhuk

Reputation: 91

C++ Pass lambda to template parameter

How to pass lambda as template parameter.
For example this code

template<void (*callback)()>
void function() {
    callback();
}

int main() {
    function<[]() -> void { std::cout << "Hello world\n"; }>();
}

fails with error "invalid template argument for 'function', expected compile-time constant expression".
What I'm doing wrong.

Edit
I want to implement something like this

template<typename T,
        T (*deserializer)(buffer *data),
        void (*serializer)(T item, buffer *data)>
class Type {
public:
    T item;

    Type(T item) : item(item) {
    }

    Type(buffer *data) {
        deserialize(data);
    }

    void serialize(buffer *data) {
        serializer(item, data);
    }

    void deserialize(buffer *data) {
        deserializer(data);
    }
};

typedef Type<int, [](buffer* data) -> int { return -1; }, [](int item, buffer* data) -> void {}> IntType

typedef Type<long, [](buffer* data) -> long { return -1; }, [](long item, buffer* data) -> void {}> LongType

Upvotes: 8

Views: 7287

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275878

Lambdas in C++14, including their conversion to function pointers, are not constexpr.

In C++17, this was changed, so now

constexpr auto tmp = []() -> void { std::cout << "Hello world\n"; };
function<+tmp>();

will definitely work. I am uncertain if

function<+[]() -> void { std::cout << "Hello world\n"; }>()

would work; there are some rules about lambdas in unevaluated contexts and inside template argument lists that may be separate from the constexpr lambda problem and may apply here.

We can hack it in C++14.

Create a template class that stores a static copy of a lambda and exposes a static function with the same signature (f_ptr) that calls that static copy of a lambda.

Instantiate it once globally with your lambda.

Pass a pointer to the f_ptr to your template.

So:

template<class L> struct stateless; // todo
template<class L> stateless<L> make_stateless(L l){return std::move(l);}

auto foo = make_stateless( []() -> void { std::cout << "Hello world\n"; } );

function< &foo::f_ptr >();

this is almost certainly not what you want.

Upvotes: 9

sp2danny
sp2danny

Reputation: 7687

The kind of template in the example does not take a type as a parameter, but rather a value. This value needs to be determinable at runtime, in order to instantiate the template, and the value of a lambda is not a compile time constant, so this method just wont do. The common way of sending functors to a function is:

template<typename Func>
void foo(Func&& f)
{
    f();
}

And since you want a class template (please put such information in the question, not the comments), here is an example using classes:

#include <utility>

template<typename Func>
class MyClass
{
public:
    MyClass(Func&& f) : f(f) {}
    void Run() { f(); }
private:
    Func f;
};

template<typename Func>
MyClass<Func> MakeMyClass(Func&& f)
{
    return { std::forward<Func>(f) };
}

int main()
{
    auto x = MakeMyClass( [](){} );
    x.Run();
}

Upvotes: 2

Related Questions