6

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
KoHcoJlb
  • 71
  • 3
  • 6
  • I'm planning to use it with class templates, this is just test code. – KoHcoJlb Feb 01 '17 at 18:46
  • I was about to close the question as a duplicate of [that one](http://stackoverflow.com/questions/3810519/how-to-use-a-lambda-expression-as-a-template-parameter), but neither the question nor the answers there are stellar... – Quentin Feb 01 '17 at 21:38

2 Answers2

7

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

In C++17, this is going to change. There are no stable compilers with that feature implemented that I'm aware of (if you find one, can you mention it in the comments below?).

At that point

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Related: this works in clang, but current gcc has a bug and incorrectly rejects the lamba because it has no linkage (neither global nor `static`). [Can I use the result of a C++17 captureless lambda constexpr conversion operator as a function pointer template non-type argument?](https://stackoverflow.com/q/47606810) has a link to the GCC bug report. – Peter Cordes Aug 07 '18 at 22:42
  • 1
    I am confused by the syntax `function<+tmp>()`. Naively, I wanted to write: `function()`, but that doesn't work. Why is the `+` needed here? – Kai Petzke Sep 06 '18 at 23:03
  • 3
    @KaiPetzke `+` does promotion of certain types; like, `+true` is `int` with value `1` because `true` is promoted to `int` by unary `+`. In the case of lambdas, it acts as a promote-to-function-pointer-with-matching-signature. This is more true by accident than design -- any class with an implicit operator that converts to a single pointer type works likes this -- but it turns out to be useful. – Yakk - Adam Nevraumont Sep 06 '18 at 23:44
1

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();
}
sp2danny
  • 7,488
  • 3
  • 31
  • 53