Skip to content

Instantly share code, notes, and snippets.

@marekjm
Created August 10, 2015 19:22
Show Gist options
  • Save marekjm/1900b3dcff9924e81815 to your computer and use it in GitHub Desktop.
Save marekjm/1900b3dcff9924e81815 to your computer and use it in GitHub Desktop.
An example showing that without guidance (static_cast<>) functional will sometimes not handle inheritance
// Simple test for using std::function with inheritance
#include <functional>
#include <iostream>
class Base
{
public:
virtual void do_it(const std::string & s)
{
std::cout << "Doing " << s << std::endl;
}
};
class Derived : public Base
{
public:
void do_it(const std::string & s) override
{
for (int i = 0; i < 5; ++i)
{
std::cout << i << ": ";
Base::do_it(s);
}
}
virtual void dont_do_it(const std::string & s) {
std::cout << "Not doing " << s << std::endl;
}
};
typedef std::function<void(const std::string &)> mfunc;
typedef std::function<void(Base*, const std::string &)> bfunc;
typedef void (Base::*bmethod)(const std::string &);
int main()
{
using namespace std::placeholders;
Base b;
Derived d;
mfunc f1 = std::bind(&Base::do_it, b, _1);
mfunc f2 = std::bind(&Base::do_it, d, _1);
f1("important work...");
f2("important work 5 times");
auto f3 = std::bind(&Base::do_it, b, _1);
f3("a bit of time off...");
auto f4 = std::bind(&Derived::dont_do_it, d, _1);
f4("important work...");
mfunc f5 = std::bind(&Derived::do_it, d, _1);
f5("more important work...");
// now, all is fine and dandy up to this point as we have the objects to bind at our disposal.
// however, what if we *don't yet have* the object we will have to call the function on?
// we must *store* the member pointer for later use, because we don't have an object we could bind our function to.
// no problems with assigning base class member pointer.
// everything works as expected.
bfunc f6 = &Base::do_it;
// works on Base class object...
f6(&b, "less important work...");
// ...and with Derived class object
f6(&d, "less important work...");
// we can even std::bind stored functions if we want to
auto f6b = std::bind(f6, &b, _1);
f6b("even less important work...");
auto f6d = std::bind(f6, &d, _1);
f6d("even less important work...");
/*
we are out of luck if we want to store a pointer to member of derived class in a variable
that holds pointers to members of base class, though.
and for good reason!
imagine the situation when some crazy person would call such a derived-class-only function on a
base class pointer... horrible, terrible, awful things would happen (most likely a segfault).
uncomment below to see the compiler question your intelligence and programming proficiency (or just
remingin you of that not-so-safe thing you'd like to do).
*/
//bfunc f7 = &Derived::do_it;
// however, it we tell the compiler that:
//
// 1) we are all consenting adults and we want to engage in the acts of perverse pleasures (i.e. taking pointers to members),
// 2) we promise to not use the members irresponsibly (never-ever),
// 3) and that we *know* that bad things will happen if we apply the member to an object of a wrong class,
// 4) that we still wanna do it,
//
// it will let us because, heck, if we are too stubborn to be reasoned with then we can go compile ourselves and
// the compiler doesn't take responsibility for what will happend (it will only yell at us later "I told you, DIDN'T I?!").
bfunc f7 = static_cast<bmethod>(&Derived::dont_do_it);
// but in the end, by the virtue of dynamic dispatch we are still able to call the member, provided we do it on an
// object of correct class!
// the compiler loves us, so even if it does not approve of our actions it will still help us by generating valid code...
// __ __
// / \/ \
// \ / We love you too, compiler!
// \ /
// \ / for(;;) yours, Programmers <3
// \/
//
f7(&d, "any important work... Just chillin' with my buddy, Mr. Compiler :-)");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment