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

store a reference to a std::function object

34 views
Skip to first unread message

Christof Warlich

unread,
Feb 12, 2016, 6:17:29 AM2/12/16
to
Hi,

I'm really desperate to understand why the following code does not work as expected:

#include <functional>
#include <iostream>
struct Container {
Container(const std::function<void()> &funtor): _functor(funtor) {}
const std::function<void()> &_functor;
};
struct Functor {
Functor(int i): _i(i) {}
void operator()() {
std::cout << _i << std::endl;
}
int _i;
};
int main() {
Functor funtor(42);
Container container(funtor);
container._functor();
return 0;
}

It prints 0 instead of 42, and I really don't understand why.

It works fine when I store the std::function object as a copy instead of just storing a reference in my container, i.e. when I remove the ampersend from line 5. But that's not an option because I finally want my operator() to return a result in one of the functors member variables.

I'm obviously having some severe misconception w.r.t. the lifetime of the reference: Can anyone help me to understand what's really going on here?

Thank you so much,

Chris

Alf P. Steinbach

unread,
Feb 12, 2016, 6:53:11 AM2/12/16
to
On 2/12/2016 12:17 PM, Christof Warlich wrote:
>
> I'm really desperate to understand why the following code does not work as expected:
>
> #include <functional>
> #include <iostream>
> struct Container {
> Container(const std::function<void()> &funtor): _functor(funtor) {}
> const std::function<void()> &_functor;
> };
> struct Functor {
> Functor(int i): _i(i) {}
> void operator()() {
> std::cout << _i << std::endl;
> }
> int _i;
> };
> int main() {
> Functor funtor(42);
> Container container(funtor); //!!
> container._functor();
> return 0;
> }
>
> It prints 0 instead of 42, and I really don't understand why.

It could just as well have crashed; it's Undefined Behavior.

You're binding a reference to a temporary in the line I've marked "!!".
The temporary's lifetime extends to the semicolon in that line. From
then on you have a /dangling reference/.

Note also that the convention with leading underscore for data member
names is in direct conflict with the rules for reserved names. There are
zillion naming conventions to choose from. No need to choose the worst.


Cheers & hth.,

- Alf

Christof Warlich

unread,
Feb 12, 2016, 7:22:32 AM2/12/16
to
Am Freitag, 12. Februar 2016 12:53:11 UTC+1 schrieb Alf P. Steinbach:
> > Container container(funtor); //!!
...
> It could just as well have crashed; it's Undefined Behavior.
>
> You're binding a reference to a temporary in the line I've marked "!!".
> The temporary's lifetime extends to the semicolon in that line. From
> then on you have a /dangling reference/.

Again, just to fully understand: Is the temporary created because the compiler silently converts my Functor object to a (temporary) std::function<void()> object while passing to my container?

>
> Note also that the convention with leading underscore for data member
> names is in direct conflict with the rules for reserved names. There are
> zillion naming conventions to choose from. No need to choose the worst.

Ok, I'll happily take this advice as well :-).

Alf P. Steinbach

unread,
Feb 12, 2016, 8:14:42 AM2/12/16
to
On 2/12/2016 1:22 PM, Christof Warlich wrote:
> Am Freitag, 12. Februar 2016 12:53:11 UTC+1 schrieb Alf P.
> Steinbach:
>>> Container container(funtor); //!!
> ...
>> It could just as well have crashed; it's Undefined Behavior.
>>
>> You're binding a reference to a temporary in the line I've marked
>> "!!". The temporary's lifetime extends to the semicolon in that
>> line. From then on you have a /dangling reference/.
>
> Again, just to fully understand: Is the temporary created because the
> compiler silently converts my Functor object to a (temporary)
> std::function<void()> object while passing to my container?

Yes. The `Container` class should instead be like

struct Container
{
function<void()> functor_;

Container( function<void()> functor )
: functor_( move( functor ) )
{}
};

An alternative is to accept whatever type of functor the client code
supplies, without invoking the (possibly costly for some types) type
erasure of `std::function`, e.g.

template< class Func >
struct Container
{
Func functor_;

Container( Func functor )
: functor_( move( functor ) )
{}
};

template< class Func >
auto make_container( Func f )
-> Container<Func>
{ return Container<Func>( move( f ) ); }

In addition to possible improved efficiency for bloated but movable
functors, this has the general advantage of not throwing away type
information. Which in itself can also improve efficiency, because it
allows the compiler to generate inline code for a call on class type
functor. However, before being swayed by such arguments I'd measure, and
most likely (and yes, I've made that decision a few times) I wouldn't
care but just use std::function for simplicity and /clarity/.

K. Frank

unread,
Feb 15, 2016, 5:56:13 PM2/15/16
to
Hello Christof!

I've been tripped up in a similar way using std::function<>.
As Alf pointed out and you've concluded, when you call
your Container constructor and pass it a Functor

1) the compiler creates a temporary std::function<> that
contains, in essence, a copy of your Functor, and

2) when you take a reference to this std::function, you
end up with a dangling reference.

What I've done in the past to get the behavior I think
you want is to use std::ref.

Here's a modified version of your example that illustrates
this:

#include <iostream>
using std::cout;
using std::endl;

#include <functional>

struct Container {
Container (std::function<void()> functor) : functor_(functor) {}
std::function<void()> functor_;
};

struct Functor {
Functor (int i) : i_(i) {}
void operator()() {
cout << i_++ << endl;
}
int i_;
};

int main() {
Functor functor(42);
functor();
functor();
Container container(std::ref(functor));
container.functor_();
container.functor_();
functor();
return 0;
}

Notice that I've added the i_++, so that the state in
Functor gets modified.

I've purposely called functor() and container.functor_()
multiple times to illustrate that you pass the state
contained in functor into container, and that you get the
modified state out again, not only in container.functor_,
but in functor, as well.

That is, you really do get reference semantics to your
original functor.

Notice that Container now uses value semantics, and that
the client of container has to explicitly use reference
semantics via std::ref to get the desired behavior. This
is presumably an imperfection in Container's interface,
but I think it's the price you have to pay for using
std::function, which is inherently value-based.

To comment a little bit further:

std::function seems like a great idea -- it holds anything
that "acts like a function." So you can use functions,
explicit function objects, lambdas, etc.

But when would you want to use a function object instead of
a plain function? The common reason is because you want your
function object to contain some state. You can use the value
semantics of std::function, and, in effect, make a copy of
your function object if all you want to do is pass state in
(a common use of function objects), but not if you want to
get (mutable) state back out into your original function object.

The only way I know how to do this (in a reasonably
straightforward way) with std::function is to make the client
explicitly use reference semantics with std::ref.

This seems to be a limitation of std::function (not that I
have a proposal to fix it), and it tripped me up along the
same lines you describe in your post when I first tried to
take advantage of the convenience of std::function.

> Thank you so much,
>
> Chris


Happy Hacking!


K. Frank
0 new messages