Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda as template parameter

I found those similar questions Lambda expressions as class template parameters and How to use a lambda expression as a template parameter?, but even with the answers available I didnt get why the following code isn't working (g++4.8.2 and g++-4.9):

auto GoLess = [](int a,int b) -> bool
{
        return a < b;
};


template<typename Order>
struct foo
{
        int val;
        bool operator<(const foo& other)
        {
                return Order(val, other.val);
        }
};

typedef foo<decltype(GoLess)> foo_t;

int main()
{
        foo_t a,b;
        bool r = a < b;
}

The compiler output is:

test.cpp: In instantiation of ‘bool foo<Order>::operator<(const foo<Order>&) [with Order = <lambda(int, int)>]’:
test.cpp:26:15:   required from here
test.cpp:17:30: error: no matching function for call to ‘<lambda(int, int)>::__lambda0(int&, const int&)’
   return Order(val, other.val);
                              ^
test.cpp:17:30: note: candidates are:
test.cpp:5:16: note: constexpr<lambda(int, int)>::<lambda>(const<lambda(int, int)>&)
 auto GoLess = [](int a,int b) -> bool
                ^
test.cpp:5:16: note:   candidate expects 1 argument, 2 provided
test.cpp:5:16: note: constexpr<lambda(int, int)>::<lambda>(<lambda(int, int)>&&)
test.cpp:5:16: note:   candidate expects 1 argument, 2 provided

Shouldn't this code work? Reading from those other threads in my understanding this code should compile, but is not.

Thanks a lot

Addendum:

To clarify a little, in on of the above questions KennyTM wrote the following code:

auto comp = [](const A& lhs, const A& rhs) -> bool { return lhs.x < rhs.x; };
auto SetOfA = std::set <A, decltype(comp)> (comp);

Which should work, the second argument to std::set is a 'comparator' which in this case is a lambda, in my code I'm trying to do the same or at least i think i'm doing the same, but my code is not working. Something i missing in my code?

Please note also from Xeo in Lambda expressions as class template parameters

auto my_comp = [](const std::string& left, const std::string& right) -> bool {
  // whatever
}

typedef std::unordered_map<
  std::string,
  std::string,
  std::hash<std::string>,
  decltype(my_comp)
  > map_type;

Again this should work. Where's my fault?

Thanks

like image 864
fjanisze Avatar asked Feb 19 '15 21:02

fjanisze


People also ask

Can you template a lambda?

Lambda-expressions are not allowed in unevaluated expressions, template arguments, alias declarations, typedef declarations, and anywhere in a function (or function template) declaration except the function body and the function's default arguments.

Can lambda be Constexpr?

Visual Studio 2017 version 15.3 and later (available in /std:c++17 mode and later): A lambda expression may be declared as constexpr or used in a constant expression when the initialization of each data member that it captures or introduces is allowed within a constant expression.

How do you pass a lambda function in C++?

Permalink. All the alternatives to passing a lambda by value actually capture a lambda's address, be it by const l-value reference, by non-const l-value reference, by universal reference, or by pointer.

Can lambdas be Inlined?

Inlinable lambdas can only be called inside inline functions or passed as inlinable arguments. noinline lambdas, however, can be manipulated in any way you like, including being stored in fields or passed around.


1 Answers

As the compiler is helpfully telling you, the problem is with this line:

return Order(val, other.val);

Since Order is a type (and not a function), that is calling Order's two-argument constructor. But it doesn't have one.

The correct syntax for invoking a functional class is:

return Order()(val, other.val);

However, that won't work either because the class generated for the lambda has a deleted default constructor.

In short, you need to instantiate your class with (the only) instance of the lambda.

Here's one possible way to proceed:

template<typename Order>
struct foo {
    foo(Order compare) : compare_(compare) {}
    bool operator<(const foo& other) {
        return compare_(val, other.val);
    }
    int val;
    Order compare_;
};

/* In practice, you'll want to template a parameter pack
 * for the other arguments to the constructor, since those are likely.
 * Also, you might want to use std::forward.
 */
template<typename Order>
foo<Order> foomaker(Order order) {
    return foo<Order>(order);
}

int main() {
    auto GoLess = [](int a,int b) -> bool
        {
             return a < b;
        };

    auto a = foomaker(GoLess);
    auto b = foomaker(GoLess);
    bool r = a < b;
    return r;
}
like image 168
rici Avatar answered Sep 22 '22 05:09

rici