9

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.

JoeG
  • 12,994
  • 1
  • 38
  • 63
Ayjay
  • 3,413
  • 15
  • 20
  • I guess you could use `const decltype(v)::value_type& f` which could be shorter for very long types. It's too bad we use `auto` for the argument type. Anyway, all this crap you've added is gross and bad practice IMO. – David Oct 21 '11 at 02:26
  • Well obviously all the grossness would be hidden off in a header somewhere. The only bit remaining would be the actual lambda itself, plus 1 line after any type that you wanted to use this with. – Ayjay Oct 21 '11 at 02:32
  • 2
    You're saying you did all this to shave off one character? What's wrong with `for_each(x.begin(), x.end(), [](foo f) {f->f();});`? – R. Martinho Fernandes Oct 21 '11 at 03:19
  • 1
    (a) because then it ties the algorithm less to the data types used, (b) because the compiler *can* work out the type, and therefore should, and (c) because, when types get complicated, it starts saving a *lot* more and vastly reduces line clutter and boilerplate: `for_each(x.begin(), x.end(), [&](const first_type& a, const second_type& b) { return a + b; }` versus `for_each(x.begin(), x.end(), [](a, b) { return a + b; }` saves nearly 40 characters! – Ayjay Oct 21 '11 at 03:29
  • 1
    Or when you start operating on maps, `for_each(m.begin(), m.end(), [&](const map::value_type& x) {...}` versus `for_each(m.begin(), m.end(), [&](x) {...}` – Ayjay Oct 21 '11 at 03:31
  • 7
    A little bit of background info: Herb Sutter said that the C++ committee considered adding "auto" as a parameter type specifically for this reason. However, they concluded that normal functions would have to support this as well. And because they feared that such a huge change, especially that late in the C++11 development, would have delayed the whole standard, they left it out completely. – ltjax Oct 21 '11 at 11:59
  • from what this blog post suggests you have to wait for C++14 to achieve that. http://scottmeyers.blogspot.co.at/2013/05/c14-lambdas-and-perfect-forwarding.html – Alexander Oh Oct 16 '13 at 06:48

6 Answers6

14

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) {...});
Martin Ba
  • 37,187
  • 33
  • 183
  • 337
  • 3
    @Ayjay: Hey, why not? :-P (I've no clue whether compilers will swallow this. It was just for fun.) – Martin Ba Oct 24 '11 at 07:03
  • I would *love* having some easy-to-type greek letters in my code. I don't know if my coworkers would though... – Ayjay Oct 24 '11 at 22:56
  • 2
    The old "space cadet" keyboard used by the MIT Lisp machine actually had a special lambda key: http://en.wikipedia.org/wiki/Space-cadet_keyboard – Crashworks Jun 18 '12 at 07:39
  • 2
    The standard does not allow for unicode characters in source code. – Sebastian Mach Aug 06 '12 at 15:23
  • @phresnel Really? Not even in string literals? Because I do it *very* often. – leemes Sep 30 '12 at 16:46
  • 1
    @Ayjay Let's write a renaming script as a pre-build step for this! :) [jk] – leemes Sep 30 '12 at 16:47
  • 3
    @phresnel - Of course the standard allows implementations to support unicode source characters. It does not require it, but it does not forbid it either. – Martin Ba Sep 30 '12 at 17:15
  • @MartinBa: After relooking into the standard, I am not sure anymore. At least I wouldn't bet on them, though. gcc gives an error for the lambda one. – Sebastian Mach Oct 01 '12 at 14:27
  • @MartinBa: Found it in the standard: "The set of physical source file characters accepted is implementation-defined.". Personally, unless forced, I would avoid anything else than ASCII 7bit. – Sebastian Mach Oct 01 '12 at 14:40
3

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) { ... });
Viktor Sehr
  • 12,825
  • 5
  • 58
  • 90
  • 1
    I wouldn't say they were idiots, but I have wondered how many work with C++ on a daily basis. – graham.reeds Oct 21 '11 at 09:32
  • 1
    The problem with auto in lambdas is that it would make the lambda object itself polymorphic, but in C++ any object (expression) must have a well defined compiler-time type. What should happen if I write: `auto lambda = [](auto x) { return x; }; lambda(1); lambda("foo");`? Maybe it could resolve to a `template operator()(T x)` or something? But the committee is now wary of including complex functionality without any real world experience (_concepts_, _exceptions specifications_, _external templates_). – rodrigo Oct 21 '11 at 11:05
  • rodrigo: I see your point, but, they could have made anonymous lambdas work with auto as it would make the languages so much more convenient. Let's face it, the reason c++ is slowly fading is because they never seems to incorporate functionality that are useful for most of the c++ programmers. – Viktor Sehr Oct 21 '11 at 11:23
  • @ViktorSehr But then you would have to define what an anonymous lambda is. And then, the rules for resolving the `auto` types... Remember that currently `auto` is usable only for automatic (local) variables, not for function parameters. I think that it is more feasible the _lambda template_: `for_each(m.begin(), m.end(), template [](T x) { ... });`. – rodrigo Oct 21 '11 at 12:25
  • 7
    That's really unfair. The Committee very much wanted to see polymorphic lambdas in C++11, and they were included in the original proposal and several revisions thereof. Unfortunately, ultimately they had to be dropped from C++11 because of too many unresolved issues, too little time. The committee is conservative, yes, and prefers no feature X over broken feature X, but that hardly makes them idiots. – JohannesD Oct 21 '11 at 14:27
  • JohannesD: Okay, it's a bit unfair maybe to call them idiots, but, sometimes I still get the feeling they don't work with C++ on a daily basis. Most programmers wont benefit from const_expr and better unions, however, a remake of algorithms (in a new namesapce maybe) which takes containers and auto in lambdas would be a lot more utilized for the main bunch c++ programmers. – Viktor Sehr Oct 21 '11 at 16:38
  • By algorithms accepting containers I mean boost::range. Same goes for C++98, I mean, why only implement the auto_ptr when a shared_ptr is so easily implemented. – Viktor Sehr Oct 21 '11 at 16:40
  • Also, add to that, automatical move-construc\assign generation if no explicit constructor (or something) – Viktor Sehr Oct 21 '11 at 16:42
2

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; });
bames53
  • 86,085
  • 15
  • 179
  • 244
  • I wonder if it would work if we treated a templated lambda as a non-templated functor with a templated function call operator. – Vaughn Cato Oct 22 '11 at 02:43
  • I suppose syntax like `[]template(T t) { }` would be ugly but workable. If you mean using the `auto` keyword to do this it would probably have to work for normal function parameters as well and it'd be a new template syntax, which I don't think is the right way to go. – bames53 Oct 22 '11 at 18:15
  • This would not require any special logic, merely allowing lambdas to optionally generate template functors `auto lambda = [](auto x) { return x; } ` ==> `struct lambda { template T operator()(T x) {return x;} };` – Ayjay Oct 23 '11 at 22:40
  • My complaint about letting auto do that wasn't about needing special logic. My complaint was that that basically creates a new template syntax, and that the new template syntax would probably need support in other places besides lambdas, such as the parameters of normal functions. – bames53 Oct 24 '11 at 04:40
  • @bames53: Why is that? It would be great if they did, but I see no reason why it would *have* to be extended for regular functions. Lambdas already have the unique required step of creating an anonymous struct, why would changing how that struct is generated affect regular function definitions? – Ayjay Oct 24 '11 at 04:50
  • They'd need to because if they didn't you'd be able to use auto in lambdas but not in normal functions. – bames53 Oct 24 '11 at 15:25
1

You can have lambda deduce it parameters(and types) from the function, but then each function has to make that available. This discusses how that could be accomplished here:

http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/c347334187628476?hl=en

Basically you can call functions using a ruby-like way. So you would call for_each like this:

$(for_each(some_range), x)
{
    printf("%i\n", x);
};

The ($) dollar sign is a macro that takes care of deducing the type from the function. Now this won't work with std::for_each, it has to be used with a specially defined function that emits the parameter types for the lambda.

Since it uses a macro it doesn't take into account operator precedence, so you can't use it with range adapters.

Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59
0

So this compiles

#define FOR_EACH_IN(iter__, list__, ...) \
    std::for_each(list__.begin(), list__.end(), [&](decltype(*list__.begin()) iter__) __VA_ARGS__)

Given:

auto DoSomethingTo = [](T AnElement){ /* insert profitable activity */ };
std::array<T, n> AnArray;

You can do:

FOR_EACH_IN(AnElement, AnArray,
{
    DoSomethingTo(AnElement);
});

The JavaScript fanboy inside me is excited by how it looks but as a C++ coder I'm horrified to have come up with something like this. It's likely to trip lots of static analyses and linters, plus when something goes wrong I hope you enjoy debugging macros.

But, hey, it's clean.

0

If you're willing to use macros and use all that setup wouldn't a shortcut macro using decltype be sufficient? Something like:

#define T_FOR_EACH( begin_, end_, var_, func_ ) \
  for_each( (begin_), (end_), [](decltype(*begin_) var_) func_ )

Then you could have:

T_FOR_EACH(x.begin(), x.end(), &f, {f->f();} );

The reason I don't put & for the variable in the define is that with this format would can still specify all the type specifiers you want and whether it is a reference or copy.

Excuse me if the syntax is wrong, I don't have a C++11 compiler around I can test any of this with, it's just an idea.

edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267