0

I'd like to downcast a pointer to a member function, where the base class is inherited virtually. This leads to an error at compile time.

In my application, I have a collection of several objects (e.g. D1, D2), that have a common base class B. They are held together and kept track of by means of a std::vector<B*>.

The derived objects introduce double-returning functions that need to be called on a regular basis. The base class keeps track of these functions by means of a std::vector<double(B::*)(void)>. The whole chain is utilized in the second to last line of the main function.

Therefore, I needed to cast the function pointers when adding them to the vector, as is done in the constructor of the derived classes D1 and D2. his is successful in case of D1, but not in case of D2, as B is a virtual base of D2. As I learned, a static cast does not work in this case (error message 1), because the deriving classes D21 and D22 only have a pointer to the instance of B. However, a dynamic_cast also leads to an error at compile time (error message 2), saying that the target is not of pointer type.

error message 1: error: pointer to member conversion via virtual base 'B' v.push_back(static_cast(&D2::f2));

error message 2: error: cannot dynamic_cast '&D2::f2' (of type 'double (class D2::)()') to type 'double (class B::)()' (target is not pointer or reference)

I'd like to know how, if at all, I can perform the desired cast. If not I'd like to know why such a thing is not possible.

Edit: Some background: I integrate a system of ordinary differential equations numerically. The derived classes represent the individual equations of the system, and each derived class computes its own state. By collecting the classes in the vector o, I can quickly modify the system of equations. The vector v in the base class is needed for output purposes. During integration, each subsystem also takes care of its own output through the base class. The diamond arose because I separated two often used pieces of code into two individual classes.

Arguably, there are other ways to design the class hierarchy, and there are other ways to achieve my goal (in this case I made the function p in B virtual, and re-implemented it in the deriving classes). However, I could not fathom why the cast failed, so I asked the question.

MWE:

    #include <vector>
    #include <algorithm>
    #include <iostream>

    // Base class
    class B {
    protected:
      std::vector<double(B::*)(void)> v;
    public:
      void p ( void )
      {for_each(v.begin(), v.end(), [this] (double (B::*f) (void)) {std::cerr << (this->*f)() << std::endl;});};
    };

    // Normal inheritance
    class D1: public B
    {public:
      double f1 ( void ) { return 1; }
      D1() { v.push_back(static_cast<double(B::*)(void)>(&D1::f1));} // Casting is successful here.
    };

    // Setting up 'the diamond'
    class D21: virtual public B {};
    class D22: virtual public B {};
    class D2: public D21, public D22
    {public:
      double f2 ( void ) { return 2; }
      D2() { v.push_back(dynamic_cast<double(B::*)(void)>(&D2::f2)); } // How to cast here?
    };

    int main ()
    {
      // Vector holding the classes together
      std::vector<B*> o;

      // Set up the system
      D1 d1;
      D2 d2;
      o.push_back(&d1);
      o.push_back(&d2);

      // Derived functions are called
      for_each(o.begin(),o.end(),[] (B *o) {o->p();});

      return 0;
    }
  • 4
    Can you please elaborate on *why* you want to do something like this? What is the *original* problem? What's the use-case you want to solve by doing this? ([Related reading about the XY problem](http://xyproblem.info/)) – Some programmer dude Nov 03 '16 at 08:34
  • Possible duplicate of [When should static\_cast, dynamic\_cast, const\_cast and reinterpret\_cast be used?](http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used) – giant_teapot Nov 03 '16 at 08:39
  • ```dynamic_cast``` doesn't handle the "diamond shaped" inheritance patterns very well. Using ```reinterpret_cast``` (which is the "I know what I'm doing" cast) should do the trick. – giant_teapot Nov 03 '16 at 08:42
  • I read the answers [here](http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used), and still do not know why I cannot use `dynamic_cast`, since I use virtual inheritance, and I'm afraid to use `reinterpret_cast`. This works in the MWE, but I tried it yesterday in the complete application, and it did not compile. Now I feel that if I used it, and it worked, it's bound to fail some time. – Ser Jothan Chanes Nov 03 '16 at 10:20

1 Answers1

3

I believe it should be possible to implement the cast that you tried to use. However, it is an especially difficult corner case, which is likely the reason why it's not allowed:

You can think of member pointers as offsets into the object (non-virtual case) or into the vtable (virtual case). This works fine as long as the relevant layout of the object is fixed. However, with a virtual base, the position of the virtual base within the object may depend on the concrete subclass. More specifically, a class derived of D2 may have its virtual base at a different offset than D2 itself.

Now, when you cast a member pointer to a pointer relative to one of its base classes, the pointer needs to be adjusted. This is usually a simple addition of the relative offsets, which is a compile time constant. But since the location of the virtual base within D2 is not known at compile time, the adjustment would need to first discover this offset dynamically.

As I said, this is not impossible to implement. However, it adds significant complexity to the pointer adjustment code in the compilers, just to support an exceedingly rare corner case. And I guess that's the reason why it's not allowed.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • Thanks! I can accept the explanation that it would be a too complex corner case to implement. What confused me was that I thought of 'pointer' and 'pointer-to-member-function' as the same thing, with respect to the second error message. I'll wait a little more before I mark this answer as accepted. – Ser Jothan Chanes Nov 03 '16 at 10:25
  • Should the gist of this answer be incorporated in the answer to this [related question](http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used), maybe by clarifying that "any polymorphic type" does not include pointers to member functions? – Ser Jothan Chanes Nov 04 '16 at 09:54
  • @SerJothanChanes I don't think so. That question has enough to do to sort the different types of casts apart, it does not do to confuse readers there with special corner cases where *no* cast works. That's one of the beauties of this question and answer format: Every answer can include exactly the amount of detail that is required to answer the question at hand. And your question asks for a lot more in-depth detail than the related question you linked. Detail that is irrelevant for explaining the differences between the different types of casts. – cmaster - reinstate monica Nov 04 '16 at 21:46