Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Templated Lambda Functions?

414 views
Skip to first unread message

Andy Champ

unread,
Mar 29, 2011, 10:51:34 PM3/29/11
to

They're not allowed are they? Any ideas why not?

I have a function that takes the ends of a set, in the form of a pair of
iterators, and picks items out until it is happy - something like

void Func(const SomeIteratorType & first, const SomeIteratorType & last)
{
SomeIteratorType iter;
bool notdone = true;

for (iter = first; iter != last && notdone; ++iter)
{
// some processing
}
}

I'd like to be able to pass sometimes a forward iterator, and sometimes
a backwards one, and write it as a lambda so it has access to the other
local variables. I've ended up with a templated free function with
half-a-dozen parameters :(

VS2010, if that makes a difference.

Thanks

Andy


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Michael

unread,
Mar 30, 2011, 6:40:07 PM3/30/11
to

On Mar 29, 9:51 pm, Andy Champ <no....@nospam.invalid> wrote:
> They're not allowed are they? Any ideas why not?

I believe because the standards committee didn't want to bite off more
than the compiler writers could chew. Lambdas are a novel feature and
they wanted to make sure they got the basics right first. Maybe in C+
+2X we'll get it.


Mike

Noah Roberts

unread,
Mar 30, 2011, 6:42:19 PM3/30/11
to

On 3/29/2011 7:51 PM, Andy Champ wrote:
>
> They're not allowed are they? Any ideas why not?

Nope. Because they're unnecessary and don't make sense.

>
> I have a function that takes the ends of a set, in the form of a pair of
> iterators, and picks items out until it is happy - something like
>
> void Func(const SomeIteratorType & first, const SomeIteratorType & last)
> {
> SomeIteratorType iter;
> bool notdone = true;
>
> for (iter = first; iter != last && notdone; ++iter)
> {
> // some processing
> }
> }
>
> I'd like to be able to pass sometimes a forward iterator, and sometimes
> a backwards one, and write it as a lambda so it has access to the other
> local variables. I've ended up with a templated free function with
> half-a-dozen parameters :(
>

There's no reason why you can't write it as a lambda function. A lambda function is created on the spot and will use the types of the template function containing it. For example:

template < typename ForwardIter >
void fun(ForwardIter begin, ForwardIter end)
{
int i = 0;
typedef typename std::iterator_traits<ForwardIter>::value_type value_type;
std::for_each(begin,end, [&i](value_type const& vt) { .... });
}

You might have to work around some compiler bugs here, especially if using VS, but it's supposed to work.

What you seem to desire though is something reusable in two different places under different conditions. This is what templates are for, but not what lambda functions are for. You need some named object for reuse and in this case you want some kind of template.

If you're attempting to use a bunch of local variables then you probably want a functor with state rather than a "free function with half-a-dozen parameters".

--
http://crazycpp.wordpress.com

SG

unread,
Mar 30, 2011, 6:40:15 PM3/30/11
to

On 30 Mrz., 04:51, Andy Champ wrote:
> [subject: templated lambda functions?]

> They're not allowed are they? Any ideas why not?

If you mean lambdas with templated function call operator: no, they
don't exist. They are mentioned here and there as "polymorphic
lambdas" in C++0x discussions (as opposed to "monomorphic lambdas" we
already got). But I did not see any proposal for polymorphic lambdas.
At least I cannot recall having read proposals of this kind.

> I have a function that takes the ends of a set, in the form of a pair of
> iterators, and picks items out until it is happy - something like
>
> void Func(const SomeIteratorType & first, const SomeIteratorType & last)
> {
> SomeIteratorType iter;
> bool notdone = true;
> for (iter = first; iter != last && notdone; ++iter)
> {
> // some processing
> }
> }
>
> I'd like to be able to pass sometimes a forward iterator, and sometimes
> a backwards one, and write it as a lambda so it has access to the other
> local variables. I've ended up with a templated free function with
> half-a-dozen parameters :(

Unfortunately, I don't understand your problem. What is "it" in "and
write it as a lambda"?

SG

Daniel Krügler

unread,
Mar 31, 2011, 9:27:17 AM3/31/11
to

Am 30.03.2011 04:51, schrieb Andy Champ:
>
> They're not allowed are they? Any ideas why not?
>
> I have a function that takes the ends of a set, in the form of a pair of
> iterators, and picks items out until it is happy - something like
>
> void Func(const SomeIteratorType & first, const SomeIteratorType & last)
> {
> SomeIteratorType iter;
> bool notdone = true;
>
> for (iter = first; iter != last && notdone; ++iter)
> {
> // some processing
> }
> }
>
> I'd like to be able to pass sometimes a forward iterator, and sometimes
> a backwards one, and write it as a lambda so it has access to the other
> local variables. I've ended up with a templated free function with
> half-a-dozen parameters :(

I must say that I read your contribution several times and still did not
really get what you want. So I need to guess a bit:

The grammar of lambdas does not directly support to define a lambda
as a template, e.g. you cannot write something like

void foo() {
template<class T>
[](T t) { return t; }
}

This makes sense, because a lambda closure declaration is an object
declaration and C++ does not have object templates.

This does not mean, that lambda closure cannot be used in a template
environment. E.g. consider you have the following function template:

template<class Iter>
void bar(Iter first, Iter end) {
auto fun = [some_captures](some_args) { // OK, fun is a lambda closure
/do something/
}
fun(actual_args);
}

I've not understood what your actual use case is. If you meant that you
want to define Func as a function template (like as I did with bar) this
works as shown above.

It would help, if you would try to give a better example, if this wasn't
what you were asking for.

HTH & Greetings from Bremen,

Daniel Krügler

Mathias Gaunard

unread,
Mar 31, 2011, 9:28:03 AM3/31/11
to

On Mar 30, 4:51 am, Andy Champ <no....@nospam.invalid> wrote:
> They're not allowed are they?

Unfortunately, no.


> Any ideas why not?

Local structures are not allowed to have template members either; I
assume that's linked.


>
> I have a function that takes the ends of a set, in the form of a pair of
> iterators, and picks items out until it is happy - something like
>
> void Func(const SomeIteratorType & first, const SomeIteratorType & last)
> {
> SomeIteratorType iter;
> bool notdone = true;
>
> for (iter = first; iter != last && notdone; ++iter)
> {
> // some processing
> }
>
> }
>
> I'd like to be able to pass sometimes a forward iterator, and sometimes
> a backwards one, and write it as a lambda so it has access to the other
> local variables. I've ended up with a templated free function with
> half-a-dozen parameters :(

Just use function objects.
You can use a lambda DSEL to generate one.

Andy Champ

unread,
Apr 1, 2011, 11:48:36 AM4/1/11
to

On 31/03/2011 14:28, Mathias Gaunard wrote:
>
> Just use function objects.
> You can use a lambda DSEL to generate one.
>

Could you elaborate? Google at first sight thinks I'm talking about
lambda sensors on Diesel engines, then pointed me at something
"Lambda-style anonymous functions for C++ in less than 500 lines of
code" which is obviously completely OTT for my requirements!

Thanks

Andy

Andy Champ

unread,
Apr 1, 2011, 11:48:08 AM4/1/11
to

On 30/03/2011 23:42, Noah Roberts wrote:
>
> On 3/29/2011 7:51 PM, Andy Champ wrote:
>>
>> They're not allowed are they? Any ideas why not?
>
> Nope. Because they're unnecessary and don't make sense.
>

The position I have (a function that wants to do something similar with
an iterator and a reverse_iterator) is proof that, even if they are
rarely necessary, they are sometimes necessary.

In what way do they not make sense?

<snip>


>
> There's no reason why you can't write it as a lambda function. A lambda
> function is created on the spot and will use the types of the template
> function containing it.

The parent function is not templated. It makes two calls, one using an
iterator, and one a reverse_iterator.

> For example:
>
> template < typename ForwardIter >
> void fun(ForwardIter begin, ForwardIter end)
> {
> int i = 0;
> typedef typename std::iterator_traits<ForwardIter>::value_type value_type;
> std::for_each(begin,end, [&i](value_type const& vt) { .... });
> }
>
> You might have to work around some compiler bugs here, especially if
> using VS, but it's supposed to work.
>

Actually VS2010 seems to be a pretty good compiler. The dev environment
is a big step back from Vs2008, but the compiler is much better. Some
of our binaries come out a quarter of the size once the optimiser has run...

> What you seem to desire though is something reusable in two different
> places under different conditions. This is what templates are for, but
> not what lambda functions are for. You need some named object for reuse
> and in this case you want some kind of template.
>

Here's a worked, compilable example:

#include <set>

using namespace std;

void main()
{
set<int> coll;

for (size_t x = 0; x< 100; ++x) coll.insert(rand());

auto howManyMake1000 = [&](set<int>::iterator first,
set<int>::iterator last)->size_t
{
int x = 0;
int count = 0;

for (set<int>::iterator where = first; where != last && x
< 1000; ++where,++count)
x += *where;
return count;
};

size_t howMany = howManyMake1000(coll.begin(), coll.end());
}

In my real code the lambda needs access to a bunch of local variables to
do its magic - not least the logging object! - and I want another line
which the equivalent of

size_t howManyEnd = howManyMake1000(coll.rbegin(), coll.rend());

> If you're attempting to use a bunch of local variables then you probably
> want a functor with state rather than a "free function with half-a-dozen
> parameters".
>

I don't think I follow you there. How do the parameters get into the
functor?

Andy


--

Mathias Gaunard

unread,
Apr 2, 2011, 6:16:16 PM4/2/11
to

On Apr 1, 5:48 pm, Andy Champ <no....@nospam.invalid> wrote:
> On 31/03/2011 14:28, Mathias Gaunard wrote:
>
>
>
>> Just use function objects.
>> You can use a lambda DSEL to generate one.
>
> Could you elaborate?

Boost.Lambda, Boost.Phoenix, etc.

Mathias Gaunard

unread,
Apr 2, 2011, 6:15:49 PM4/2/11
to

On Apr 1, 5:48 pm, Andy Champ <no....@nospam.invalid> wrote:
> On 30/03/2011 23:42, Noah Roberts wrote:
>
>
>
>> On 3/29/2011 7:51 PM, Andy Champ wrote:
>
>>> They're not allowed are they? Any ideas why not?
>
>> Nope. Because they're unnecessary and don't make sense.
>
> The position I have (a function that wants to do something similar with
> an iterator and a reverse_iterator) is proof that, even if they are
> rarely necessary, they are sometimes necessary.
>
> In what way do they not make sense?

Of course they make sense, they have a huge quantity of applications.
Polymorphism is one of the big features of functional programming that
is not possible with C++0x lambdas.

Vidar Hasfjord

unread,
Apr 2, 2011, 6:16:04 PM4/2/11
to

On Apr 1, 4:48 pm, Andy Champ <no....@nospam.invalid> wrote:
> [...]

> Here's a worked, compilable example:
>
> #include <set>
>
> using namespace std;
>
> void main()
> {
> set<int> coll;
>
> for (size_t x = 0; x< 100; ++x) coll.insert(rand());
>
> auto howManyMake1000 = [&](set<int>::iterator first,
> set<int>::iterator last)->size_t
> {
> int x = 0;
> int count = 0;
>
> for (set<int>::iterator where = first; where != last && x
> < 1000; ++where,++count)
> x += *where;
> return count;
> };
>
> size_t howMany = howManyMake1000(coll.begin(), coll.end());
>
> }
>
> In my real code the lambda needs access to a bunch of local variables to
> do its magic - not least the logging object! - and I want another line
> which the equivalent of
>
> size_t howManyEnd = howManyMake1000(coll.rbegin(), coll.rend());

Since you clearly need a named object here I don't see lambdas as the
solution. That said, I see that it would be somewhat convenient to
declare a template function locally, but this is unfortunately not
allowed.

The only reason you need a template here is to handle the different
iterator types. If you can separate the iteration from the rest of the
logic of your function and reformulate the latter as a functor, then
you can call upon the standard library to do the iteration. You can
pass the functor whatever state and references it needs to do its job
(including a reference to your logging object).

For example:

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

using namespace std;

int main () {
vector <int> c (10);
generate (c.begin (), c.end (), &rand);
for_each (c.begin (), c.end (), [] (int i) {cout << i << " ";});
cout << endl;

struct WithinPartialSum {
int target_sum, partial_sum;

WithinPartialSum (int sum)
: target_sum (sum), partial_sum (0) {}

bool operator () (int i)
{return (partial_sum += i) <= target_sum;}
};

const int s = 100000;
int nf = count_if (c.begin (), c.end (), WithinPartialSum (s));
int nb = count_if (c.rbegin (), c.rend (), WithinPartialSum (s));

cout << "Number of items within partial sum " << s << ": \n"
<< "forwards: " << nf << ", backwards: " << nb << endl;
cin.get (); // pause
return 0;
}

Regards,
Vidar Hasfjord

Andy Champ

unread,
Apr 5, 2011, 5:23:02 PM4/5/11
to

On 02/04/2011 23:16, Mathias Gaunard wrote:
>
> On Apr 1, 5:48 pm, Andy Champ<no....@nospam.invalid> wrote:
>> On 31/03/2011 14:28, Mathias Gaunard wrote:
>>
>>> Just use function objects.
>>> You can use a lambda DSEL to generate one.
>>
>> Could you elaborate?
>
> Boost.Lambda, Boost.Phoenix, etc.
>
>

Hi Mathias,

I went away and did some reading.

It looks to me as if I'll have to declare a local closure<> to describe
which variables are exposed to the world of Phoenix. The Phoenix Lambda
can then pick them up - but this means that those local variables
required inside the lambda will have to be mentioned explicitly. That's
probably neater than passing them all explicitly - but not much neater.

And I can see another problem - we don't use Boost, never mind Phoenix.
So my code review is going to end up with me preparing an introductory
lecture on the Phoenix library for the team before writing the 10 lines
of code that use it.

Now the code might be half the size with Phoenix - but half of 20 lines
isn't worth that much effort :/

I've come to like Lambdas in the little time I've been using them (since we switched compilers). I've probably written for_each more times in the last month than the preceding 5 years - it finally makes sense.

Thanks

Andy

Mathias Gaunard

unread,
Apr 6, 2011, 3:41:16 PM4/6/11
to

On Apr 5, 11:23 pm, Andy Champ <no....@nospam.invalid> wrote:

> It looks to me as if I'll have to declare a local closure<> to describe
> which variables are exposed to the world of Phoenix.

No you don't. All terminals within the lambda-expression are caught by
value unless wrapped by the 'ref' function, in which case they're
caught by reference.
There is not even a closure class or function in the library, so I
have no idea where you're going at.


> And I can see another problem - we don't use Boost, never mind Phoenix.
> So my code review is going to end up with me preparing an introductory
> lecture on the Phoenix library for the team before writing the 10 lines
> of code that use it.

This might prove difficult indeed. People are reluctant to using
complicated template stuff like DSELs.


> Now the code might be half the size with Phoenix - but half of 20 lines
> isn't worth that much effort :/

Right, which is why I suggested using function objects.
You don't have to use a DSEL to generate them if you don't want to,
you can simply write them yourself. It's easy and it requires no
dependencies, but it must happen at namespace scope.

Andy Champ

unread,
Apr 6, 2011, 6:18:18 PM4/6/11
to
On 06/04/2011 20:41, Mathias Gaunard wrote:
>
> On Apr 5, 11:23 pm, Andy Champ<no....@nospam.invalid> wrote:
>
>> It looks to me as if I'll have to declare a local closure<> to describe
>> which variables are exposed to the world of Phoenix.
>
> No you don't. All terminals within the lambda-expression are caught by
> value unless wrapped by the 'ref' function, in which case they're
> caught by reference.
> There is not even a closure class or function in the library, so I
> have no idea where you're going at.
>

I don't have the references I looked at here - but closures.hpp seems to
be the file.

http://www.boost.org/doc/libs/1_37_0/boost/spirit/home/classic/phoenix/closures.hpp

>
>> And I can see another problem - we don't use Boost, never mind Phoenix.
>> So my code review is going to end up with me preparing an introductory
>> lecture on the Phoenix library for the team before writing the 10 lines
>> of code that use it.
>
> This might prove difficult indeed. People are reluctant to using
> complicated template stuff like DSELs.
>
>

We make quite heavy use of templates already. The problem here is that
these are someone else's templates, and they arrive in a big lump.

>> Now the code might be half the size with Phoenix - but half of 20 lines
>> isn't worth that much effort :/
>
> Right, which is why I suggested using function objects.
> You don't have to use a DSEL to generate them if you don't want to,
> you can simply write them yourself. It's easy and it requires no
> dependencies, but it must happen at namespace scope.
>
>

The "nice thing" about a lambda for this problem is that it exists in
the local context, and can access local variables. As soon as I leave
the context (as for a namespace-scope class) I've lost that access. So
I have to feed all the data into the object or function by hand.

But that's what I've had to do.

The other nice thing I'm finding, as we've recently moved to VS2010 with
its lambda support, is that std::for_each finally makes sense!

Andy

Mathias Gaunard

unread,
Apr 10, 2011, 5:47:32 AM4/10/11
to
On Apr 7, 12:18 am, Andy Champ <no....@nospam.invalid> wrote:
> On 06/04/2011 20:41, Mathias Gaunard wrote:
>
>
>
> > On Apr 5, 11:23 pm, Andy Champ<no....@nospam.invalid> wrote:
>
> >> It looks to me as if I'll have to declare a local closure<> to describe
> >> which variables are exposed to the world of Phoenix.
>
> > No you don't. All terminals within the lambda-expression are caught by
> > value unless wrapped by the 'ref' function, in which case they're
> > caught by reference.
> > There is not even a closure class or function in the library, so I
> > have no idea where you're going at.
>
> I don't have the references I looked at here - but closures.hpp seems to
> be the file.
>
> http://www.boost.org/doc/libs/1_37_0/boost/spirit/home/classic/phoeni...

This looks like an undocumented experimental feature, since it's not
present in the latest version of the documentation of Phoenix v2 or
v3.

In any case, that's definitely not something for "normal" usage.


> > This might prove difficult indeed. People are reluctant to using
> > complicated template stuff like DSELs.
>
> We make quite heavy use of templates already. The problem here is that
> these are someone else's templates, and they arrive in a big lump.

How can you be making "heavy use" of template meta-programming
techniques without using Boost or duplicating a lot of things it does?


> The other nice thing I'm finding, as we've recently moved to VS2010 with
> its lambda support, is that std::for_each finally makes sense!

I personally prefer to use a foreach macro, it's more flexible (you
can break, continue, return...), it's shorter, and it doesn't require
a C++0x compiler.

Mathias Gaunard

unread,
Apr 10, 2011, 5:47:44 AM4/10/11
to
On Apr 7, 12:18 am, Andy Champ <no....@nospam.invalid> wrote:

> I don't have the references I looked at here - but closures.hpp seems to
> be the file.
>

> http://www.boost.org/doc/libs/1_37_0/boost/spirit/home/classic/phoeni...

This is a reference to Phoenix v1, this is completely outdated (how
did you even end up with this? Googling gives the docs of v2)

Latest Phoenix version is v3 (trunk only), latest version in a release
is v2. (v2 and v3 are very similar in terms of interface)

0 new messages