Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Achieving clean lambda functions in C++11

I've been playing around a lot with the new C++11 lambda's, and the requirement to fully specify the template argument is a real drag. The syntax I would like to be using is something similar to the following:

#include <vector>
#include <algorithm>

struct foo
{
    void bar() {}
};

int main()
{
    vector<foo> v(10);

    for_each(v.begin(), v.end(), [](f) {f.bar();});
                                   ^^^
}

Is there any way to get anything approximately close to this? Boost's Phoenix library is OK, but the syntax for calling member functions requires lots of boiler plate - I guess I'm after C++11's ease of calling member functions coupled with Phoenix's automatic deduction of type.

Current idea

I have gotten it down to this syntax:

vector<foo> x(1);
vector<bar> y(1);
for_each(x.begin(), x.end(), [](_a f) {f->f();});
for_each(y.begin(), y.end(), [](_a b) {b->b();});

Which works, but you have to add the capability per type (eg. ADD_AUTO_LAMBDA_SUPPORT(foo);). It also has the limitation that all supported types can not have any ambiguous members.

The full code for that is:

#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

struct foo
{
    foo() : x(3) {}
    int x;
    void f() { cout << x << endl;}
};

struct bar
{
    bar() : y(133.7) {}
    double y;
    void b() { cout << y << endl;}
};

struct combo : foo, bar { };

struct _a
{
    _a(foo& f) : offset(reinterpret_cast<combo*>(&f)) {}
    _a(bar& b) : offset(reinterpret_cast<combo*>((char*)&b - 2*sizeof(foo))) {}

    combo* operator->() { return offset; }

private:
    combo* offset;
};

int main()
{
    vector<foo> x(1);
    vector<bar> y(1);

    for_each(x.begin(), x.end(), [](_a f) {f->f();});
    for_each(y.begin(), y.end(), [](_a b) {b->b();});
}

You could then use some template and preprocessor magic to generate both _a and combo, but the issue comes when you have ambiguous names (eg. a third struct with a b() function - you need a way to disambiguate them that I can't think of at the moment.

like image 420
Ayjay Avatar asked Oct 21 '11 00:10

Ayjay


3 Answers

Note: I fully agree that [](auto f){ ... } would be very desirable!

While we don't have that, what about good old typedef? It just adds one line, is very "low-tech" and makes the lambda easy to read:

typedef const map<key_type, value_type>::value_type&  λp_t;
for_each(m.begin(), m.end(), [&](λp_t x) {...});
like image 68
Martin Ba Avatar answered Oct 21 '22 02:10

Martin Ba


You can use decltype:

for_each(m.begin(), m.end(), [&](decltype(*m.begin()) x){...});

But it really, really sucks that you cant use auto in anonymous lambdas.

Update:

You could also do

#define _A(CONTAINER_NAME) decltype(*CONTAINER_NAME.begin())
for_each(m.begin(), m.end(), [&](_A(m) x) { ... });
like image 32
Viktor Sehr Avatar answered Oct 21 '22 01:10

Viktor Sehr


So, presumably in the case of the following:

std::array<int,10> a;
for_each(begin(a),end(a),[](auto i){ /* ... */ });

You want the compiler to figure out that the lambda takes an int and basically read this as:

for_each(begin(a),end(a),[](int i){ /* ... */ });

The issue is that the type of the lambda affects the type of the for_each template instantiation, which might choose a different specialization, which could in turn require a different type deduction for the lambda parameter. So there is simply no reasonable way for the compiler to use the algorithm code to automatically deduce the type of the arguments you pass in.

Anyway, at least for the for_each algorithm you don't need this, just use the range for loop:

for(auto i:a) { /* ... */ }

And in other places use decltype:

transform(begin(a),end(a),begin(a),[](decltype(*begin(a)) i) { return 2*i; });
like image 20
bames53 Avatar answered Oct 21 '22 03:10

bames53