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?
source to share
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.
source to share
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);
source to share