A Positive Lambda: '+[]{}' - What Sorcery Is This

A positive lambda: '+[]{}' - What sorcery is this? [duplicate]

Yes, the code is standard conforming. The + triggers a conversion to a plain old function pointer for the lambda.

What happens is this:

The compiler sees the first lambda ([]{}) and generates a closure object according to §5.1.2. As the lambda is a non-capturing lambda, the following applies:

5.1.2 Lambda expressions [expr.prim.lambda]

6 The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

This is important as the unary operator + has a set of built-in overloads, specifically this one:

13.6 Built-in operators [over.built]

8 For every type T there exist candidate operator functions of the form

    T* operator+(T*);

And with this, it's quite clear what happens: When operator + is applied to the closure object, the set of overloaded built-in candidates contains a conversion-to-any-pointer and the closure type contains exactly one candidate: The conversion to the function pointer of the lambda.

The type of test in auto test = +[]{}; is therefore deduced to void(*)(). Now the second line is easy: For the second lambda/closure object, an assignment to the function pointer triggers the same conversion as in the first line. Even though the second lambda has a different closure type, the resulting function pointer is, of course, compatible and can be assigned.

Why would one want to put a unary plus (+) operator in front of a C++ lambda? [duplicate]

It's not a feature of lambda and more is a feature of implicit type conversion.

What happens there is stemming from the fact that a captureless lambda can be implicitly converted to a pointer to function with same signature as lambda's operator(). +[]{} is an expression where unary + is a no-op , so the only legal result of expression is a pointer to function.

In result auto funcPtr would be a pointer to a function, not an instance of an object with anonymous type returned by lambda expression. Not much of advantage in provided code, but it can be important in type-agnostic code, e.g. where some kind of decltype expression is used. E.g.

#include <type_traits>

void foo(int);

template<class T>
struct is_foo : std::is_same<T, decltype(&foo)> {};

int main()
{
auto foo1 = +[](int)->void {};
auto foo2 = [](int)->void {};
static_assert(is_foo<decltype(foo1)>::value, "foo1 is not like foo");
static_assert(is_foo<decltype(+foo2)>::value, "+foo2 is not like foo");
static_assert(is_foo<decltype(foo2)>::value, "foo2 is not like foo");
}

Note that you can do same with foo: std::is_same<T, decltype(+foo)> {};

Albeit some platforms may not support that, because they inherently may have a variety of function pointers with different calling convention and the expression will be ambiguous.

lambda function doesn't matches argument list

Two issues.

  1. The element type of the list is Student*, lambda should take Student* too.

  2. You're passing lambda to merge_func which expects a function pointer. Template argument deduction doesn't consider implicit conversion (from lambda to function pointer) and fails deducing template parameter T on the 3rd function parameter.

You can change merge_func to

template <class T>
list<T> merge_func(list<T> first_list, list<T> second_list, bool(*func)(T x, T y))

And convert lambda to function pointer when calling merge_func.

std3 = merge_func(std, std2, static_cast<bool(*)(Student* x, Student* y)>([](Student* x, Student* y)->bool {return x->_age < y->_age; }));

Or perform the conversion with sorcery operator+.

std3 = merge_func(std, std2, +[](Student* x, Student* y)->bool {return x->_age < y->_age; });

LIVE

Or prevent deduction on the 3rd function parameter.

template <class T>
list<T> merge_func(list<T> first_list, list<T> second_list, std::type_identity_t<bool(*)(T x, T y)> func)

LIVE

Detect if C++ lambda can be converted to function pointer

If you know the signature of a function you want your lambda to be converted to, you can leverage the std::is_assignable trait:

auto lambda = [] (char, double) -> int { return 0; };
using signature = int(char, double);
static_assert(std::is_assignable<signature*&, decltype(lambda)>::value, "!");

DEMO

This way it can work also with generic lambdas.


I'd like to have a solution that works for vs 2013/2015, gcc and clang

If you don't know the signature, here's an approach that is an alternative to checking whether + triggers an implicit conversion. This one exploits the std::is_assignable test and verifies whether a lambda is assignable to a function pointer with the same signature as the lambda's function call operator. (Just like a test with unary operator plus, this doesn't work with generic lambdas. But in C++11 there are no generic lambdas).

#include <type_traits>

template <typename T>
struct identity { using type = T; };

template <typename...>
using void_t = void;

template <typename F>
struct call_operator;

template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...)> : identity<R(A...)> {};

template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...) const> : identity<R(A...)> {};

template <typename F>
using call_operator_t = typename call_operator<F>::type;

template <typename, typename = void_t<>>
struct is_convertible_to_function
: std::false_type {};

template <typename L>
struct is_convertible_to_function<L, void_t<decltype(&L::operator())>>
: std::is_assignable<call_operator_t<decltype(&L::operator())>*&, L> {};

Test:

int main()
{
auto x = [] { return 5; };
auto y = [x] { return x(); };

static_assert(is_convertible_to_function<decltype(x)>::value, "!");
static_assert(!is_convertible_to_function<decltype(y)>::value, "!");
}

GCC, VC++, Clang++

Passing a lambda into a function template

Your function binsearch takes a function pointer as argument. A lambda and a function pointer are different types: a lambda may be considered as an instance of a struct implementing operator().

Note that stateless lambdas (lambdas that don't capture any variable) are implicitly convertible to function pointer. Here the implicit conversion doesn't work because of template substitution:

#include <iostream>

template <typename T>
void call_predicate(const T& v, void (*predicate)(T)) {
std::cout << "template" << std::endl;
predicate(v);
}

void call_predicate(const int& v, void (*predicate)(int)) {
std::cout << "overload" << std::endl;
predicate(v);
}

void foo(double v) {
std::cout << v << std::endl;
}

int main() {
// compiles and calls template function
call_predicate(42.0, foo);

// compiles and calls overload with implicit conversion
call_predicate(42, [](int v){std::cout << v << std::endl;});

// doesn't compile because template substitution fails
//call_predicate(42.0, [](double v){std::cout << v << std::endl;});

// compiles and calls template function through explicit instantiation
call_predicate<double>(42.0, [](double v){std::cout << v << std::endl;});
}

You should make your function binsearch more generic, something like:

template <typename T, typename Predicate>
T binsearch(const std::vector<T> &ts, Predicate p) {

// usage

for(auto& t : ts)
{
if(p(t)) return t;
}

// default value if p always returned false

return T{};
}

Take inspiration from standard algorithms library.

A positive lambda: '+[]{}' - What sorcery is this? [duplicate]

Yes, the code is standard conforming. The + triggers a conversion to a plain old function pointer for the lambda.

What happens is this:

The compiler sees the first lambda ([]{}) and generates a closure object according to §5.1.2. As the lambda is a non-capturing lambda, the following applies:

5.1.2 Lambda expressions [expr.prim.lambda]

6 The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

This is important as the unary operator + has a set of built-in overloads, specifically this one:

13.6 Built-in operators [over.built]

8 For every type T there exist candidate operator functions of the form

    T* operator+(T*);

And with this, it's quite clear what happens: When operator + is applied to the closure object, the set of overloaded built-in candidates contains a conversion-to-any-pointer and the closure type contains exactly one candidate: The conversion to the function pointer of the lambda.

The type of test in auto test = +[]{}; is therefore deduced to void(*)(). Now the second line is easy: For the second lambda/closure object, an assignment to the function pointer triggers the same conversion as in the first line. Even though the second lambda has a different closure type, the resulting function pointer is, of course, compatible and can be assigned.

Boolean lambdas?

It turns out that it is standard!

If you refer to this answer[1], non-capturing lambdas are convertible to function pointers. And it turns out again that function pointers, being pointers themselves, are implicitly convertible to bool!

4.12 Boolean conversions [conv.bool]


1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a
prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false;
any other value is converted to true. A prvalue of type std::nullptr_t can be converted to a prvalue of
type bool; the resulting value is false.

To give a supporting proof that the conversion to function pointer is what makes all of this happen, I've tried doing the same thing with capturing lambdas. Then "can't convert to bool" errors are generated.

LIVE CODE

int main() {
int i;
auto lambda = [i]{};

bool b = lambda;

if(lambda) {}
}

[1] Which, honestly, gave me the idea to write this.



Related Topics



Leave a reply



Submit