How to simulate virtuality for method template

后端 未结 5 1037
忘了有多久
忘了有多久 2021-01-19 14:08

I have a class hierarchy where I want to introduce a method template that would behave like if it was virtual. For example a simple hierarchy:

class A {
  vi         


        
相关标签:
5条回答
  • 2021-01-19 14:38

    Is there any common code you could extract and make virtual?

    class A {
      virtual ~A() {}
    
      template<typename T>
      void method(T &t) 
      {
          ...
          DoSomeWork();
          ...
      }
    
      virtual void DoSomeWork() {}
    };
    
    class B : public A {
      virtual void DoSomeWork() {}
    };
    
    0 讨论(0)
  • 2021-01-19 14:38

    As you may know, you cannot have templates for virtual functions, since the entirety of the virtual functions is part of the class type and must be known in advance. That rules out any simple "arbitrary overriding".

    If it's an option, you could make the template parameter part of the class:

    template <typename T> class A
    {
    protected:
      virtual void method(T &);
    };
    
    template <typename T> class B : public A<T>
    {
      virtual void method(T &); // overrides
    };
    

    A more involved approach might use some dispatcher object:

    struct BaseDispatcher
    {
      virtual ~BaseDispatcher() { }
      template <typename T> void call(T & t) { dynamic_cast<void*>(this)->method(t); }
    };
    struct ConcreteDispatcher : BaseDispatcher
    {
      template <typename T> void method(T &);
    };
    
    class A
    {
    public:
      explicit A(BaseDispatcher * p = 0) : p_disp(p == 0 ? new BaseDispatcher : p) { }
      virtual ~A() { delete p_disp; };
    private:
      BaseDispatcher * p_disp;
      template <typename T> void method(T & t) { p_disp->call(t); }
    };
    
    class B : public A
    {
    public:
      B() : A(new ConcreteDispatcher) { }
      // ...
    };
    
    0 讨论(0)
  • 2021-01-19 14:39

    I think the only solution is the http://en.wikipedia.org/wiki/Visitor_pattern

    See this topic: How to achieve "virtual template function" in C++

    0 讨论(0)
  • 2021-01-19 14:46

    Oops. Initially answered at the wrong question - ah well, at another question

    After some thinking I recognized this as the classic multi-method requirement, i.e. a method that dispatches based on the runtime type of more than one parameter. Usual virtual functions are single dispatch in comparison (and they dispatch on the type of this only).

    Refer to the following:

    • Andrei Alexandrescu has written (the seminal bits for C++?) on implementing multi-methods using generics in 'Modern C++ design'
      • Chapter 11: "Multimethods" - it implements basic multi-methods, making them logarithmic (using ordered typelists) and then going all the way to constant-time multi-methods. Quite powerful stuff !
    • A codeproject article that seems to have just such an implementation:
      • no use of type casts of any kind (dynamic, static, reinterpret, const or C-style)
      • no use of RTTI;
      • no use of preprocessor;
      • strong type safety;
      • separate compilation;
      • constant time of multimethod execution;
      • no dynamic memory allocation (via new or malloc) during multimethod call;
      • no use of nonstandard libraries;
      • only standard C++ features is used.
    • C++ Open Method Compiler, Peter Pirkelbauer, Yuriy Solodkyy, and Bjarne Stroustrup
    • The Loki Library has A MultipleDispatcher
    • Wikipedia has quite a nice simple write-up with examples on Multiple Dispatch in C++.

    Here is the 'simple' approach from the wikipedia article for reference (the less simple approach scales better for larger number of derived types):

    // Example using run time type comparison via dynamic_cast
    
    struct Thing {
        virtual void collideWith(Thing& other) = 0;
    }
    
    struct Asteroid : Thing {
        void collideWith(Thing& other) {
            // dynamic_cast to a pointer type returns NULL if the cast fails
            // (dynamic_cast to a reference type would throw an exception on failure)
            if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
                // handle Asteroid-Asteroid collision
            } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
                // handle Asteroid-Spaceship collision
            } else {
                // default collision handling here
            }
        }
    }
    
    struct Spaceship : Thing {
        void collideWith(Thing& other) {
            if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
                // handle Spaceship-Asteroid collision
            } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
                // handle Spaceship-Spaceship collision
            } else {
                // default collision handling here
            }
        }
    }
    

    0 讨论(0)
  • 2021-01-19 14:53

    You can use the "Curiously Recurring Template Pattern" http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

    Using this pattern, the base class takes the derived class type as a template parameter, meaning that the base class can cast itself to the derived type in order to call functions in the derived class. It's a sort of compile time implementation of virtual functions, with the added benefit of not having to do a virtual function call.

    template<typename DERIVED_TYPE>
    class A {
    public:
        virtual ~A() {}
    
        template<typename T>
        void method(T &t) { static_cast<DERIVED_TYPE &>(*this).methodImpl<T>(t); }
    };
    
    class B : public A<B>
    {
    friend class A<B>;
    
    public:
        virtual ~B() {}
    
    private:
        template<typename T>
        void methodImpl(T &t) {}
    };
    

    It can then be used like this...

    int one = 1;
    A<B> *a = new B();
    a->method(one);
    
    0 讨论(0)
提交回复
热议问题