2

While experimenting with the concepts of object slicing and polymorphism, I came up with this code example, which works as I was expecting: the function call operator of the derived FunctorTrue class is called -instead of that of the parent Functor class.

#include <iostream>
#include <functional>
using namespace std;

class Functor : public std::function<bool()> {
public:
virtual ~Functor() {};
    virtual bool operator()() const {return false;};
};

class FunctorTrue : public Functor{
public:
    bool operator()() const {return true;}
};

class Dispatcher {
public:
    const Functor & mFunctor; 
    Dispatcher(const Functor & functor): mFunctor(functor) {}
    Dispatcher(const Functor && functor): mFunctor(functor) {
    }

    void dispatch(){
        cout << boolalpha << mFunctor() << endl;
    }
};

int main() {
    Dispatcher dt = Dispatcher(FunctorTrue());
    dt.dispatch(); // returns true
}

Note: You can run this example in Ideone.

I found out that if I modify the constructor of the Dispatcher class to print out a line, the function call operator of the parent would be called instead:

class Dispatcher {
public:
    const Functor & mFunctor; 
    Dispatcher(const Functor & functor): mFunctor(functor) {}
    Dispatcher(const Functor && functor): mFunctor(functor) {
        cout << "Constructor with rvalue" << endl;
    }

    void dispatch(){
        cout << boolalpha << mFunctor() << endl;
    }
};

int main() {
    Dispatcher dt = Dispatcher(FunctorTrue());
    dt.dispatch(); // returns false
}

Note: You can also run this example in Ideone.

That unexpected behavior led me to think that, because of the type of the member variable mFunctor in the Dispatcher class, the dispatch method only knows about the "base class part" of the object, effectively calling the function call operator of the base class. But that doesn't hold true in the original example, which leaves me all confused.

My questions are:

1) Why does the change in the constructor changes the method being called in the Functor > FunctorTrue class hierarchy?

2) If the variation is related to some optimization that the default constructor does, what is that optimization and how can I specify it?

Thanks a lot in advance.

levelont
  • 584
  • 4
  • 12

1 Answers1

4

You have undefined behavior.

Your constructor is storing a reference to an object whose lifetime ends, and then dispatch() attempts to use that object.

int main() {
    Dispatcher dt = Dispatcher(FunctorTrue());
       // lifetime of FunctorTrue object ends here

    dt.dispatch(); // Use of dt.mFunctor causes undefined behavior
}
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • Thanks! This might very well be the first time I come across something similar -or the first time someone pin points that as the root cause of the issue. Undefined behavior is not something I usually keep in the back of my head as the possible cause for a bug. Your response has encouraged me to change that ^^ – levelont May 22 '17 at 09:09
  • Wait a second... How is the constness of the `mFunctor` member of the `Dispatcher` class not extending the life of that `FunctorTrue()` temporary? – levelont May 22 '17 at 16:43
  • @levelont: Temporary lifetimes aren't extended indirectly. Think about it from the point of view of main. How would it know (in general) that the constructor was going to keep a reference around? – Vaughn Cato May 23 '17 at 03:28
  • but... ain't variable life extension done by the compiler, which can "peek" into all the code's implementation and see that behind the covers, there is a const reference extending the life of a temporary? – levelont May 26 '17 at 12:33
  • @levelont: no. The temporary is bound to the `functor` argument, which is a reference. That causes the temporary to live until the end of the full expression. The fact that you are binding `functor` (which is a reference, not a temporary) to `mFunctor` does not change this. – Vaughn Cato May 26 '17 at 13:14
  • but the `functor`argument is not only a (rvalue!) reference, but a `const ref`! Wouldn't that be enough for extending the life of a temporary? P.S.: sorry for playing ping pong with this question, but I'm really interested in gaining full understanding. – levelont May 29 '17 at 09:44
  • @levelont I've created a room for further discussion: https://chat.stackoverflow.com/rooms/145388/chat-between-vaughncato-and-levelont – Vaughn Cato May 29 '17 at 15:52