3

Here is the situation. Let's say we have a virtual base class (e.g. ShapeJuggler) which contains a method that takes a shared pointer to a virtual base class object (e.g. Shape) as argument. Let's jump into the following pseudo-code to understand:

class Shape {
}
class ShapeJuggler {
    virtual void juggle(shared_ptr<Shape>) = 0;
}
// Now deriving a class from it
class Square : public Shape {
}
class SquareJuggler : public ShapeJuggler {
public:
    void juggle(shared_ptr<Shape>) {
         // Want to do something specific with a 'Square'
         // Or transform the 'shared_ptr<Shape>' into a 'shared_ptr<Square>'
    }
}
// Calling the juggle method
void main(void) {
    shared_ptr<Square> square_ptr = (shared_ptr<Square>) new Square();
    SquareJuggler squareJuggler;
    squareJuggler.juggle(square_ptr); // how to access 'Square'-specific members?
}

make_shared or dynamic/static_cast don't seem to do the job. Is it at all possible? Any ideas, suggestions?
Thanks

2
  • 1
    main() should return int, not void. Commented Mar 13, 2017 at 9:49
  • "we have a virtual base class" you forgot the virtual keyword in your example (or "pseudo-code") Commented Mar 13, 2017 at 18:54

2 Answers 2

5

This is where std::dynamic_pointer_cast (or one of its friends) comes into play.
It's just like dynamic_cast, but for std::shared_ptrs.

In your case (assuming the Shape class is polymorphic so dynamic_cast works):

void juggle(shared_ptr<Shape> shape) {
    auto const sq = std::dynamic_pointer_cast<Square>(shape);
    assert(sq);

    sq->squareSpecificStuff();
}
Sign up to request clarification or add additional context in comments.

Comments

3

This is the multiple dispatch problem. Their are many solution to this problem, the cleanest might be using the visitor pattern, but if you just have one function that need multiple dispatch you could avoid using a visitor:

class SquareJuggler;
class TriangleJuggler;
//.... others concrete jugglers.

class Shape {
    //The default behaviour for any juggler and any shape
    virtual void juggle_by(Juggler& t) {
       //default code for any shape an juggle
    }
    // list each juggler for which you may
    // implement a specific behavior
    virtual void juggle_by(SquareJuggler& t) {
      //provides default behavior in case you will not
      // create a specific behavior for a specific shape.
      //for example, just call the unspecific juggler:
      this->Shape::juggle_by(static_cast<Juggler&>(t));
    }
    virtual void juggle_by(TriangleJuggler& t) {
      //provides default behavior in case you will not
      //create a specific behavior for a specific shape.
      //for example, just call the unspecific juggler:
      this->Shape::juggle_by(static_cast<Juggler&>(t));
    }
   //...
};
// Now deriving a class from it
class Square : public Shape {
  void juggle_by(SquareJuggler& s) override{
     //code specific to SquareJuggler and Shape
  }
};
class Triangle : public Shape {
  void juggle_by(TriangleJuggler& t) override{
     //code specific to TriangleJuggler and Shape
  }
};

class ShapeJuggler {
  virtual void juggle(shared_ptr<Shape> s) {
    //by default (if default has sense):
    s->juggle_by(*this);
  }
};
class SquareJuggler: public ShapeJuggler {
public:
  void juggle(shared_ptr<Shape> s) override {
      s->juggle_by(*this);
  }
};
class TriangleJuggler: public ShapeJuggler {
public:
  void juggle(shared_ptr<Shape> s) override {
      s->juggle_by(*this);
  }
};
// Calling the juggle method
void main(void) {
    shared_ptr<Square> square_ptr = (shared_ptr<Square>) new Square();
    SquareJuggler squareJuggler;
    squareJuggler.juggle(square_ptr); 
       //This last call, will perform two virtual calls:
       //   1. SquareJuggler::juggle(shared_ptr<Shape);
       //   2. Square::juggle_by(SquareJuggler&);
}

You could also defines your XXXJuggler as final, which will enable some devirtualization optimization.

1 Comment

Thank you for taking the time to provide this very complete and clear explanation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.