C++0x lambda and capturing computed values

S

Sohail Somani

Hello,

I'm pretty sure the answer is "no" but is it possible to capture
computed values?

Specifically, I am thinking of the following case:

vector<function<void()>> functions;

for(iterator it = ...)
{
functions.push_back(boost::bind(whatever,*it););
}

Here, the "*it" sub expression passes in a computed value to boost::bind
which is then stored by value. If I change it to the following:

for(...)
{
functions.push_back([it](){whatever(*it);});
}

This is a potential bug since the iterator can become invalidated by the
time the function is called.

The undesirable alternative is:

for(...)
{
value_type & t = *it;
functions.push_back([t](){whatever(t);});
}

It would be ideal if I could do something like:

functions.push-back([t=*it](){whatever(t);});

Any thoughts or suggestions?

Thanks in advance!
 
S

SG

vector<function<void()>> functions;

for(iterator it = ...)
{
  functions.push_back(boost::bind(whatever,*it););
}

Here, the "*it" sub expression passes in a computed value to boost::bind
which is then stored by value. If I change it to the following:

for(...)
{
  functions.push_back([it](){whatever(*it);});
}

This is a potential bug since the iterator can become invalidated by the
time the function is called.

The undesirable alternative is:

for(...)
{
  value_type & t = *it;
  functions.push_back([t](){whatever(t);});
}

I had to look into the draft again to check whether it actually does
what you seem to think it does due to decltype(t) being a reference.
But yes, it captures the referred-to object by value. So, inside the
lambda's body decltype(t) will not be a reference and it'll be
equivalent to what your first version with boost::bind does.
It would be ideal if I could do something like:

functions.push-back([t=*it](){whatever(t);});

Any thoughts or suggestions?

Interesting idea. But it'll hardly save you typing. IMHO,

auto&& t = *it;
functions.push_back([=]{whatever(t);});

is an okay-solution which doesn't justify a more complicated grammar
in the capture clause.


Cheers!
SG
 
S

Sohail Somani

SG said:
The undesirable alternative is:

for(...)
{
value_type & t = *it;
functions.push_back([t](){whatever(t);});
}

I had to look into the draft again to check whether it actually does
what you seem to think it does due to decltype(t) being a reference.
But yes, it captures the referred-to object by value. So, inside the
lambda's body decltype(t) will not be a reference and it'll be
equivalent to what your first version with boost::bind does.

I had to double-check as well :)

Just to clarify, your understanding comes from [expr.prim.lambda]/7 ?
It would be ideal if I could do something like:

functions.push-back([t=*it](){whatever(t);});

Any thoughts or suggestions?

Interesting idea. But it'll hardly save you typing. IMHO,

auto&& t = *it;
functions.push_back([=]{whatever(t);});

is an okay-solution which doesn't justify a more complicated grammar
in the capture clause.

Well the actual case is something like:

for(iterator it1, it2, it3 ...)
{
.. some stuff here ..
functions.push_back(bind(whatever,*it1,*it2,*it3));
}

To me, the alternative is quite verbose.

For comparison:

for(iterator it1, it2, it3 ...)
{
auto&& t1=*it1;
auto&& t2=*it2;
auto&& t3=*it3;
functions.push_back([=](){whatever(t);});
}

or

for(iterator it1, it2, it3 ...)
{
functions.push_back([t1=*it1,t2=*it2,t3=*it3]()
{whatever(t1,t2,t3);});
}

Less typing even if it is for 6*3 characters is better if you ask me.
Seems that bind is not obsoleted by lambda (I didn't expect it to be
anyway.)
 
S

Sohail Somani

Sohail said:
for(iterator it1, it2, it3 ...)
{
functions.push_back([t1=*it1,t2=*it2,t3=*it3]()
{whatever(t1,t2,t3);});
}

Less typing even if it is for 6*3 characters is better if you ask me.
Seems that bind is not obsoleted by lambda (I didn't expect it to be
anyway.)

What about something like:

for(...)
{
functions.push_back([](){whatever(<*it1>,<*it2>,<*it3>);}
}

I dunno... Just something to avoid all the typing would be nice. I'm
sure it's not gonna happen but this is from the result of porting an app
to use C++0x lambda where possible.
 
S

SG

SG said:
I had to look into the draft again to check whether it actually does
what you seem to think it does [...]

I had to double-check as well :)
Just to clarify, your understanding comes from [expr.prim.lambda]/7 ?

In N2857.pdf it's §5.2.1/8 [expr.prim.lambda] where it says that the
"object type" in case of a reference is the type the reference refers
to.
[...]
for(iterator it1, it2, it3 ...)
{
  functions.push_back([t1=*it1,t2=*it2,t3=*it3]()
                      {whatever(t1,t2,t3);});
}

You could use std::bind here. Nobody forces you to use lambdas. :)

functions.push_back(std::bind(whatever,*it1,*it2,*it3));


Cheers!
SG
 
S

Sohail Somani

SG said:
SG said:
I had to look into the draft again to check whether it actually does
what you seem to think it does [...]
I had to double-check as well :)
Just to clarify, your understanding comes from [expr.prim.lambda]/7 ?

In N2857.pdf it's §5.2.1/8 [expr.prim.lambda] where it says that the
"object type" in case of a reference is the type the reference refers
to.
[...]
for(iterator it1, it2, it3 ...)
{
functions.push_back([t1=*it1,t2=*it2,t3=*it3]()
{whatever(t1,t2,t3);});
}

You could use std::bind here. Nobody forces you to use lambdas. :)

functions.push_back(std::bind(whatever,*it1,*it2,*it3));

I thought you had to use lambda?!!! Just kidding :)

Of course, you are right. However, once you have more than one function
call involved, you can't use bind:

functions.push_back([<something to keep *itN>](){
whatever(*it1,*it2,*it3);
something_else(*it1,*it2,*it3);
});

Here is another example:

bind(&Session::connect,config.server(),config.username(),config.password());

The equivalent in C++0x lambda is too noisy to use. I cannot hold config
by reference because of (unlikely but possible) thread-safety issues.

Anyway, I just wanted to be sure that bind is still useful and you
shouldn't feel bad for using it :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top