Is the Visitor Pattern the fastest way to differentiate parameter types in C++?
Asked Answered
D

1

8

Is the Visitor Pattern the fastest way to accomplish method parameter type identification (effectively single dispatch on a parameter, not a member's class) in C++? I might know the exact method(s) I want to invoke on elements of not-yet-know subtype, so invariably making an additional virtual method call like V::visit(A *) in A::accept(V &v) { v.visit(this); } is undesirable.

// Is the Visitor pattern recommended here?  (E inherits D inherits B.)
class Foo {
public:
  virtual void visit(B *) { result = 3; }
  virtual void visit(D *) { result = 4; }
  virtual void visit(E *) { result = 5; }
private:
  int result;
}; // class Foo

// Need to add generic interface to B and its children ...
class B {
public:
  virtual void accept(class Foo &f) { f.visit(this); }
}; // class B

I'd like something functionally equivalent the following but with O(1) cost, which is AFAIK not possible with dynamic_cast<> or typeid() ladders, since std::type_info can't be a constexpr/switchable.

// O(n) search cost might get nasty with bigger hierarchies.
int foo(B *b) {
  if (typeid(b) == typeid(B *)) { return 1; }
  if (typeid(b) == typeid(D *)) { return 2; }
  if (typeid(b) == typeid(E *)) { return 3; }
  return -1;
}

What are my options here? Thanks for the advice!

Edit: Changed sample code to feed results through field, such that multiple signatures aren't needed for different method types. Thanks, Maurice!

Final decision: In addition to not liking the mandatory double dispatch cost of the Visitor Pattern, I also wanted to avoid the interface bloat of overloading foo(), but I don't think that there is a known clean pattern to do this. I ended up just doing straight static overloads and called it a day. Anyway, my wanting to encapsulate overloading inside a function is probably a questionable goal at best. Thanks, Maurice for the response.

Diamonddiamondback answered 31/8, 2010 at 17:21 Comment(1)
for readers after C++11 : have a look at Yorel Multimethod library (which is in Boost now)Wicker
S
3

In fact, the interfaces need not be duplicated. The subclasses of the visitor can handle the details of the operation. In your case:

class Visitor {
    virtual void visit(B*) = 0;
    virtual void visit(D*) = 0;
    virtual void visit(E*) = 0;
}

class Foo: public Visitor {
private:
    int result;
public:
    void visit(B*) { result = 3; }
    void visit(D*) { result = 4; }
    void visit(E*) { result = 5; }
    int apply(A* a) {
        a->accept(this);
        return result;
    }
}

So, only one accept() method is needed in each class.

All the alternatives to the visitor pattern I can think of involve some kind of run-time searching, so yes, IMHO, the visitor pattern is the fastest way.

Shebat answered 31/8, 2010 at 17:47 Comment(1)
Ah, good point. Question intro and sample code up updated to reflect this.Diamonddiamondback

© 2022 - 2024 — McMap. All rights reserved.