Search code examples
c++templatesderived-classpointer-to-member

Pointer to member type incompatible with object type when calling a pointer to a member of a derived class


I have defined a class template such as this one:

template <const category_id_t category, class Base>
class Node : public Base
{
...
    template <typename Derived, class T>
    void on_message( const frame_t& frame, void (Derived::*call)(const T*) )
    {
        if ( frame.length == sizeof(T) )
            (this->*(call))((T*)frame.data);
    }
}

The argument category serves as a token to implement several similar classes and provide proper specialization according to specific categories. The above class is then derived like this:

template <class Base>
class Sys : public Node<CID_SYS, Base>
{
    Sys() : Node<CID_SYS, Base>() { /* ... */ }
    ....
};

Class Sys is only a class that provides an base interface to objects of category CID_SYS (enum, value = 5) and serves as a base class to the actual implementation of the interface:

class SysImpl : public Sys<CAN>
{
    ...
    /* Parse remote notifications */
    void on_notify( const state_info_t* ) { /* ... */ }
};

SysImpl sys;

Finally I have a function that calls the base class Node<CID_SYS, Base> member function on_message() like this:

void foo(const frame_t& frame)
{ sys.on_message(frame, &SysImpl::on_notify ); }

The compiler throws an error around the line (this->*(call))((T*)frame.data) saying

error: pointer to member type 'void (SysImpl::)(const state_info_t*)' is incompatible with object type 'Node<(category_id_t)5u, CAN>'

The compiler has successfully guessed what template function to call, it's just that it doesn't seem to "recognize" that this is from a derived class.

What I'd like is to call any member function of a class derived from Node<CID_SYS, CAN>, not only stand-alone functions (which works perfectly well so far, not shown in the excerpt above).

What am I missing?


Solution

  • In the on_message function the variable this is not a pointer to SysImpl, it's type is Node<CID_SYS, CAN>*. The Node template class have no member on_notify so you can't call it on an instance of Node. It must be called on an instance of Derived (which should be SysImpl).

    That's why you get the error and need to cast this to Derived*:

    (static_cast<Derived*>(this)->*(call))(...);
    

    Of course, this only works if Derived actually is derived from the Node class.