C++11 Change 'Auto' Lambda to a Different Lambda

C++11 Change `auto` Lambda to a different Lambda?

Every lambda expression creates a new unique type, so the type of your first lambda is different from the type of your second (example). Additionally the copy-assignment operator of a lambda is defined as deleted (example) so you're doubly-unable to do this. For a similar effect, you can have a be a std::function object though it'll cost you some performance

std::function<bool()> a = [] { return true; };
a = [] { return false; };

transfer lambda to another lambda with different type

You can wrap f with a lambda and forward it to testMethodOnInt.

void testMethod(std::function<void(std::pair<std::string, int>)> f) {
testMethodOnInt([&](int x) { f({"", x}); });
}

How to use lambda auto parameters in C++11

C++11 doesn't support generic lambdas. That's what auto in the lambda's parameter list actually stands for: a generic parameter, comparable to parameters in a function template. (Note that the const isn't the problem here.)

Note: C++14 does support lambdas with auto, const auto, etc. You can read about it here.

You have basically two options:

  1. Type out the correct type instead of auto. Here it is the element type of X, which is pair<double, vector<int>>. If you find this unreadable, a typedef can help.

    std::stable_sort(X.rbegin(), X.rend(),
    [](const pair<double, vector<int>> & lhs,
    const pair<double, vector<int>> & rhs)
    { return lhs.first < rhs.first; });
  2. Replace the lambda with a functor which has a call operator template. That's how generic lambdas are basically implemented behind the scene. The lambda is very generic, so consider putting it in some global utility header. (However do not using namespace std; but type out std:: in case you put it in a header.)

    struct CompareFirst {
    template <class Fst, class Snd>
    bool operator()(const pair<Fst,Snd>& l, const pair<Fst,Snd>& r) const {
    return l.first < r.first;
    }
    };
    std::stable_sort(X.rbegin(), X.rend(), CompareFirst());

Assigning a lambda within a switch statement

You can't do that because every lambda has unique type, and they can't be assigned to each other. You can use std::function instead.

std::function<int(int,int,int,int)> valueFunction = [](int s, int d, int ct, int tt) { return -1; };

C++11 std::set lambda comparison function

Yes, a std::function introduces nearly unavoidable indirection to your set. While the compiler can always, in theory, figure out that all use of your set's std::function involves calling it on a lambda that is always the exact same lambda, that is both hard and extremely fragile.

Fragile, because before the compiler can prove to itself that all calls to that std::function are actually calls to your lambda, it must prove that no access to your std::set ever sets the std::function to anything but your lambda. Which means it has to track down all possible routes to reach your std::set in all compilation units and prove none of them do it.

This might be possible in some cases, but relatively innocuous changes could break it even if your compiler managed to prove it.

On the other hand, a functor with a stateless operator() has easy to prove behavior, and optimizations involving that are everyday things.

So yes, in practice I'd suspect std::function could be slower. On the other hand, std::function solution is easier to maintain than the make_set one, and exchanging programmer time for program performance is pretty fungible.

make_set has the serious disadvantage that any such set's type must be inferred from the call to make_set. Often a set stores persistent state, and not something you create on the stack then let fall out of scope.

If you created a static or global stateless lambda auto MyComp = [](A const&, A const&)->bool { ... }, you can use the std::set<A, decltype(MyComp)> syntax to create a set that can persist, yet is easy for the compiler to optimize (because all instances of decltype(MyComp) are stateless functors) and inline. I point this out, because you are sticking the set in a struct. (Or does your compiler support

struct Foo {
auto mySet = make_set<int>([](int l, int r){ return l<r; });
};

which I would find surprising!)

Finally, if you are worried about performance, consider that std::unordered_set is much faster (at the cost of being unable to iterate over the contents in order, and having to write/find a good hash), and that a sorted std::vector is better if you have a 2-phase "insert everything" then "query contents repeatedly". Simply stuff it into the vector first, then sort unique erase, then use the free equal_range algorithm.

this' pointer changes in c++11 lambda

You capture start by reference, but the variable start and the contained lambda function get destroyed at the end of out().

Later the signal handler tries to call start(), but the lambda function doesn't exist anymore. Maybe the memory where its this was stored was overwritten in the mean time, causing unexpected output.

The call to print_something() doesn't crash despite of the invalid this because the function doesn't actually try to use this. The printing in the function is independent of this and the lookup of print_somethings address can happen at compile time so that calling the function doesn't access this at runtime.

Using auto in lambda function declaration

A lambda expression does not result in a std::function. Instead, it creates an unnamed unique class type and that has an overload for operator(). When you pass your lambda to A's constructor, it creates a temporary std::function object, and you store a pointer to that temporary object. When A's constructor ends, that temporary object is destroyed, leaving you with a dangling pointer.

To fix this, just get rid of using pointers. That would look like

#include <iostream>
#include <functional>

class A
{
std::function <void ()> m_Lambda;
public:
A(const std::function <void ()> lambda): m_Lambda (lambda) {}

void ExecuteLambda()
{
m_Lambda();
}
};

void main()
{
int i1 = 1;
int i2 = 2;

const auto lambda = [&]()
{
std::cout << "i1 == " << i1 << std::endl;
std::cout << "i2 == " << i2 << std::endl;
};

A a(lambda);
a.ExecuteLambda();
}

Type of a lambda function, using auto

Lambdas are meant to be used with either auto or as template parameter. You never know the type of a lambda and you can't type it. Each lambda has it's own unique type. Even if you knew the name of the type, their type names usually contains character prohibited in type names.

Why does lambda have their own type? because in reality, the compiler create a class defined a bit like that:

struct /* unnamed */ {

// function body
auto operator()(int* a) const {
std::cout << *a << std::endl;
}

} print_int; // <- instance name

This code is very close to an equivalent (I omitted conversion operator).
As you can see, you already use auto, because lambdas are deducing the return type.

Some will say to use std::function<void(int*)>, but I disagree. std::function is a polymorphic wrapper around anything callable. Since lambdas are callable types, they fit into it. I other words, it work much like std::any but with a call operator. It will induce overhead in your application.

So what should you do?

use auto! auto isn't bad. In fact, it can even make your code faster and reduce unnecessary typing. If you feel uncomfortable with auto, well you shouldn't! auto is great, especially if you don't have the choice ;)

In fact, you could avoid using auto by using a template parameter:

template<typename F, typename Arg>
void parametric_print(F function, Arg&& arg) {
function(std::forward<Arg>(arg));
}

Then use it like this:

int main() {
int a = 3;
parametric_print([](int* a) {std::cout << *a << std::endl;}, &a);
}

There you go, no auto! However, a template parameter is deduced with the same rule as auto. In fact, concept are accepted into the C++20 standard with terse function templates. You could write the same function template like this:

// C++20
void parametric_print(auto function, auto&& arg) {
function(std::forward<decltype(arg)>(arg));
}

As mentionned by Oktalist, if concepts are accepted into the standard, then you could replace auto with Callable:

Callable print_int = [](int* a) { std::cout << *a << std::endl; };

But it does not result in a different type, it just enforce some rules when deducing the type.

What is a lambda expression in C++11?

The problem

C++ includes useful generic functions like std::for_each and std::transform, which can be very handy. Unfortunately they can also be quite cumbersome to use, particularly if the functor you would like to apply is unique to the particular function.

#include <algorithm>
#include <vector>

namespace {
struct f {
void operator()(int) {
// do something
}
};
}

void func(std::vector<int>& v) {
f f;
std::for_each(v.begin(), v.end(), f);
}

If you only use f once and in that specific place it seems overkill to be writing a whole class just to do something trivial and one off.

In C++03 you might be tempted to write something like the following, to keep the functor local:

void func2(std::vector<int>& v) {
struct {
void operator()(int) {
// do something
}
} f;
std::for_each(v.begin(), v.end(), f);
}

however this is not allowed, f cannot be passed to a template function in C++03.

The new solution

C++11 introduces lambdas allow you to write an inline, anonymous functor to replace the struct f. For small simple examples this can be cleaner to read (it keeps everything in one place) and potentially simpler to maintain, for example in the simplest form:

void func3(std::vector<int>& v) {
std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

Lambda functions are just syntactic sugar for anonymous functors.

Return types

In simple cases the return type of the lambda is deduced for you, e.g.:

void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) { return d < 0.00001 ? 0 : d; }
);
}

however when you start to write more complex lambdas you will quickly encounter cases where the return type cannot be deduced by the compiler, e.g.:

void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}

To resolve this you are allowed to explicitly specify a return type for a lambda function, using -> T:

void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) -> double {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}

"Capturing" variables

So far we've not used anything other than what was passed to the lambda within it, but we can also use other variables, within the lambda. If you want to access other variables you can use the capture clause (the [] of the expression), which has so far been unused in these examples, e.g.:

void func5(std::vector<double>& v, const double& epsilon) {
std::transform(v.begin(), v.end(), v.begin(),
[epsilon](double d) -> double {
if (d < epsilon) {
return 0;
} else {
return d;
}
});
}

You can capture by both reference and value, which you can specify using & and = respectively:

  • [&epsilon, zeta] captures epsilon by reference and zeta by value
  • [&] captures all variables used in the lambda by reference
  • [=] captures all variables used in the lambda by value
  • [&, epsilon] captures all variables used in the lambda by reference but captures epsilon by value
  • [=, &epsilon] captures all variables used in the lambda by value but captures epsilon by reference

The generated operator() is const by default, with the implication that captures will be const when you access them by default. This has the effect that each call with the same input would produce the same result, however you can mark the lambda as mutable to request that the operator() that is produced is not const.



Related Topics



Leave a reply



Submit