Why does passing an object obtained from std :: function by reference cause the program to crash?

If you pass the object obtained from std :: function to another function by reference, the program always crashes at runtime with the error bad_function_call. As shown in the following code:

#include<functional>
#include<iostream>

class foo :public std::function<int(void)> {
    public:
        int operator()(){return 1;}
};

void bar(const std::function<int(void)>& f) {std::cout << f() << std::endl;}

int main(){
    foo f;
    bar(f);
}

      

However, if the function object is passed by value like this:

void bar(std::function<int(void)> f)

      

the program works fine. I tested the program in gcc, clang and visual studio and the results are the same. What is causing this bad_function_call?

+3


source to share


3 answers


std::function::operator()

is not virtual.

class foo :public std::function<int(void)> {
  public:
    int operator()(){return 1;}
};

      

when viewed foo

like std::function

, operator()

which you wrote, nothing . You have an empty one std::function<int()>

, not one that returns 1.

std::function

uses erasure-based polymorphism, not inheritance polymorphism. It can store whatever it can call, copy and destroy. You can pass it by value and the stored callable will be associated with it.

Inheriting from it is usually not what you want to do.

class foo {
  public:
    int operator()(){return 1;}
};

      

This can be converted to std::function

. In fact, with this change, your code compiles and works.

Without this change, the base is preferred and passes a reference to the (empty) base std::function

in the argument. Instead of inheriting, it tries to convert foo

to std::function

and succeed instead .

Of course, this one is foo

pretty silly.

Instead of this:

int main(){
  foo f;
  bar(f);
}

      



we can do it:

int main(){
  auto f = []{ return 1; };
  bar(f);
}

      

and it works just as well. (The lambda above automatically generates a class that is almost identical to the type foo

above and does not inherit from std::function

.)

C ++ supports several types of polymorphism. Inheritance-based polymorphism does not allow (easily) resolving value types, and C ++ thrives on value types, so it std::function

was written to work with value types.


Like @TC below,

void bar(std::function<int(void)> f)

      

this works because the compiler chooses the base class "slicing" and uses the conversion constructor, and the conversion constructor is preferred by the C ++ standard.

void bar(std::function<int(void)> const& f)

      

not preferable here because no conversion needs to be done, just "treat as base" and this is a higher priority than creating a new object in the rules.

In the case where we pass a lambda or "unparented" foo, the "take a reference to the parent" case is not available, so a temporary one std::function

is created from ours foo

(or lambda) and f

binds to it.

+8


source


Your overload is not used, const ( from std::function

) is used with const std::function<int(void)>& f

. and since you are not initializing std::function

it is empty and then thrown when called operator ()

.

When you pass by value, from your functor (like any normal functor) is created std::function

and then std::function

called your stored functor.



But don't get out of it std::function

, just use

auto f = [](){ return 1; };
bar(f);

      

+4


source


bar(f);

      

You are passing the f

type foo

to bar

, but the parameter bar

isconst std::function<int(void)>

There foo

is increased to const std::function<int(void)>

.

You haven't overloaded operator () in const std::function<int(void)>

.

So it throws a runtime error.

+1


source







All Articles