Reputation: 715
If I pass an object that's derived from std::function to another function by reference, the program always crashes during runtime with a bad_function_call error. As the following code shows:
#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 functional object is passed by value like this:
void bar(std::function<int(void)> f)
the program runs ok. I've tested the program on gcc, clang and visual studio and the results are the same. What is causing this bad_function_call?
Upvotes: 3
Views: 887
Reputation: 275976
std::function::operator()
is not virtual.
class foo :public std::function<int(void)> {
public:
int operator()(){return 1;}
};
when viewing a foo
as a std::function
, the operator()
you wrote does nothing. You have an empty std::function<int()>
, not one that returns 1.
std::function
does type-erasure based polymorphism, not inheritance based polymorphism. It can store anything it can invoke, copy and destroy. You can pass it around by value, and the stored callable will be brought around with it.
Inheriting from it is usually not what you want to do.
class foo {
public:
int operator()(){return 1;}
};
This can be converted into a std::function
. In fact, with this change your code compiles and works.
Without this change, it prefers to cast-to-base and pass a reference to the (empty) base std::function
to the argument. Without inheritance, it instead attempts to convert the foo
to a std::function
and succeeds.
Of course, this foo
is pretty silly.
Instead of this:
int main(){
foo f;
bar(f);
}
we can do this:
int main(){
auto f = []{ return 1; };
bar(f);
}
and it works just as well. (The lambda above auto-generates a class that is almost identical to the foo
type above in the important ways. It doesn't inherit from std::function
either.)
C++ supports more than one kind of polymorphism. Inheritance based polymorphism doesn't (easily) permit value-types, and C++ thrives on value-types, so std::function
was written to work with value-types.
As @T.C. noted below,
void bar(std::function<int(void)> f)
this works because the compiler is choosing between "slicing" to the base class and using a converting constructor, and the converting constructor is preferred by the C++ standard.
void bar(std::function<int(void)> const& f)
here it isn't preferred, because no conversion need be done, just "treat as base", and that is higher priority than constructing a new object in the rules.
In the case where we pass a lambda or an "unparented" foo, the "take a reference to parent" case isn't available, so a temporary std::function
is created from our foo
(or lambda) and f
binds to it.
Upvotes: 8
Reputation: 218343
Your overload is not used, the const one (from std::function
) is used with const std::function<int(void)>& f
.
and as you don't initialize std::function
, it is empty and then throws when operator ()
is called.
When you pass by value, a std::function
is created from your functor (as any regular functor), and then std::function
invoke your stored functor.
But don't derive from std::function
, just use
auto f = [](){ return 1; };
bar(f);
Upvotes: 4
Reputation: 1853
bar(f);
You are passing f
of type foo
to bar
, but parameter of bar
is const std::function<int(void)>
Here foo
is upcasted to const std::function<int(void)>
.
You haven't overloaded operator() in const std::function<int(void)>
.
So it is throwing runtime error.
Upvotes: 1