Can a class member function template be virtual?

前端 未结 13 936
旧时难觅i
旧时难觅i 2020-11-22 03:29

I have heard that C++ class member function templates can\'t be virtual. Is this true?

If they can be virtual, what is an example of a scenario in which one would

相关标签:
13条回答
  • 2020-11-22 04:08

    The following code can be compiled and runs properly, using MinGW G++ 3.4.5 on Window 7:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    template <typename T>
    class A{
    public:
        virtual void func1(const T& p)
        {
            cout<<"A:"<<p<<endl;
        }
    };
    
    template <typename T>
    class B
    : public A<T>
    {
    public:
        virtual void func1(const T& p)
        {
            cout<<"A<--B:"<<p<<endl;
        }
    };
    
    int main(int argc, char** argv)
    {
        A<string> a;
        B<int> b;
        B<string> c;
    
        A<string>* p = &a;
        p->func1("A<string> a");
        p = dynamic_cast<A<string>*>(&c);
        p->func1("B<string> c");
        B<int>* q = &b;
        q->func1(3);
    }
    

    and the output is:

    A:A<string> a
    A<--B:B<string> c
    A<--B:3
    

    And later I added a new class X:

    class X
    {
    public:
        template <typename T>
        virtual void func2(const T& p)
        {
            cout<<"C:"<<p<<endl;
        }
    };
    

    When I tried to use class X in main() like this:

    X x;
    x.func2<string>("X x");
    

    g++ report the following error:

    vtempl.cpp:34: error: invalid use of `virtual' in template declaration of `virtu
    al void X::func2(const T&)'
    

    So it is obvious that:

    • virtual member function can be used in a class template. It is easy for compiler to construct vtable
    • It is impossible to define a class template member function as virtual, as you can see, it hard to determine function signature and allocate vtable entries.
    0 讨论(0)
  • 2020-11-22 04:13

    Templates are all about the compiler generating code at compile-time. Virtual functions are all about the run-time system figuring out which function to call at run-time.

    Once the run-time system figured out it would need to call a templatized virtual function, compilation is all done and the compiler cannot generate the appropriate instance anymore. Therefore you cannot have virtual member function templates.

    However, there are a few powerful and interesting techniques stemming from combining polymorphism and templates, notably so-called type erasure.

    0 讨论(0)
  • 2020-11-22 04:14

    No they can't. But:

    template<typename T>
    class Foo {
    public:
      template<typename P>
      void f(const P& p) {
        ((T*)this)->f<P>(p);
      }
    };
    
    class Bar : public Foo<Bar> {
    public:
      template<typename P>
      void f(const P& p) {
        std::cout << p << std::endl;
      }
    };
    
    int main() {
      Bar bar;
    
      Bar *pbar = &bar;
      pbar -> f(1);
    
      Foo<Bar> *pfoo = &bar;
      pfoo -> f(1);
    };
    

    has much the same effect if all you want to do is have a common interface and defer implementation to subclasses.

    0 讨论(0)
  • 2020-11-22 04:17

    While an older question that has been answered by many I believe a succinct method, not so different from the others posted, is to use a minor macro to help ease the duplication of class declarations.

    // abstract.h
    
    // Simply define the types that each concrete class will use
    #define IMPL_RENDER() \
        void render(int a, char *b) override { render_internal<char>(a, b); }   \
        void render(int a, short *b) override { render_internal<short>(a, b); } \
        // ...
    
    class Renderable
    {
    public:
        // Then, once for each on the abstract
        virtual void render(int a, char *a) = 0;
        virtual void render(int a, short *b) = 0;
        // ...
    };
    

    So now, to implement our subclass:

    class Box : public Renderable
    {
    public:
        IMPL_RENDER() // Builds the functions we want
    
    private:
        template<typename T>
        void render_internal(int a, T *b); // One spot for our logic
    };
    

    The benefit here is that, when adding a newly supported type, it can all be done from the abstract header and forego possibly rectifying it in multiple source/header files.

    0 讨论(0)
  • 2020-11-22 04:17

    My current solution is the following (with RTTI disabled - you could use std::type_index, too):

    #include <type_traits>
    #include <iostream>
    #include <tuple>
    
    class Type
    {
    };
    
    template<typename T>
    class TypeImpl : public Type
    {
    
    };
    
    template<typename T>
    inline Type* typeOf() {
        static Type* typePtr = new TypeImpl<T>();
        return typePtr;
    }
    
    /* ------------- */
    
    template<
        typename Calling
        , typename Result = void
        , typename From
        , typename Action
    >
    inline Result DoComplexDispatch(From* from, Action&& action);
    
    template<typename Cls>
    class ChildClasses
    {
    public:
        using type = std::tuple<>;
    };
    
    template<typename... Childs>
    class ChildClassesHelper
    {
    public:
        using type = std::tuple<Childs...>;
    };
    
    //--------------------------
    
    class A;
    class B;
    class C;
    class D;
    
    template<>
    class ChildClasses<A> : public ChildClassesHelper<B, C, D> {};
    
    template<>
    class ChildClasses<B> : public ChildClassesHelper<C, D> {};
    
    template<>
    class ChildClasses<C> : public ChildClassesHelper<D> {};
    
    //-------------------------------------------
    
    class A
    {
    public:
        virtual Type* GetType()
        {
            return typeOf<A>();
        }
    
        template<
            typename T,
            bool checkType = true
        >
            /*virtual*/void DoVirtualGeneric()
        {
            if constexpr (checkType)
            {
                return DoComplexDispatch<A>(this, [&](auto* other) -> decltype(auto)
                    {
                        return other->template DoVirtualGeneric<T, false>();
                    });
            }
            std::cout << "A";
        }
    };
    
    class B : public A
    {
    public:
        virtual Type* GetType()
        {
            return typeOf<B>();
        }
        template<
            typename T,
            bool checkType = true
        >
        /*virtual*/void DoVirtualGeneric() /*override*/
        {
            if constexpr (checkType)
            {
                return DoComplexDispatch<B>(this, [&](auto* other) -> decltype(auto)
                    {
                        other->template DoVirtualGeneric<T, false>();
                    });
            }
            std::cout << "B";
        }
    };
    
    class C : public B
    {
    public:
        virtual Type* GetType() {
            return typeOf<C>();
        }
    
        template<
            typename T,
            bool checkType = true
        >
        /*virtual*/void DoVirtualGeneric() /*override*/
        {
            if constexpr (checkType)
            {
                return DoComplexDispatch<C>(this, [&](auto* other) -> decltype(auto)
                    {
                        other->template DoVirtualGeneric<T, false>();
                    });
            }
            std::cout << "C";
        }
    };
    
    class D : public C
    {
    public:
        virtual Type* GetType() {
            return typeOf<D>();
        }
    };
    
    int main()
    {
        A* a = new A();
        a->DoVirtualGeneric<int>();
    }
    
    // --------------------------
    
    template<typename Tuple>
    class RestTuple {};
    
    template<
        template<typename...> typename Tuple,
        typename First,
        typename... Rest
    >
    class RestTuple<Tuple<First, Rest...>> {
    public:
        using type = Tuple<Rest...>;
    };
    
    // -------------
    template<
        typename CandidatesTuple
        , typename Result
        , typename From
        , typename Action
    >
    inline constexpr Result DoComplexDispatchInternal(From* from, Action&& action, Type* fromType)
    {
        using FirstCandidate = std::tuple_element_t<0, CandidatesTuple>;
    
        if constexpr (std::tuple_size_v<CandidatesTuple> == 1)
        {
            return action(static_cast<FirstCandidate*>(from));
        }
        else {
            if (fromType == typeOf<FirstCandidate>())
            {
                return action(static_cast<FirstCandidate*>(from));
            }
            else {
                return DoComplexDispatchInternal<typename RestTuple<CandidatesTuple>::type, Result>(
                    from, action, fromType
                );
            }
        }
    }
    
    template<
        typename Calling
        , typename Result
        , typename From
        , typename Action
    >
    inline Result DoComplexDispatch(From* from, Action&& action)
    {
        using ChildsOfCalling = typename ChildClasses<Calling>::type;
        if constexpr (std::tuple_size_v<ChildsOfCalling> == 0)
        {
            return action(static_cast<Calling*>(from));
        }
        else {
            auto fromType = from->GetType();
            using Candidates = decltype(std::tuple_cat(std::declval<std::tuple<Calling>>(), std::declval<ChildsOfCalling>()));
            return DoComplexDispatchInternal<Candidates, Result>(
                from, std::forward<Action>(action), fromType
            );
        }
    }
    

    The only thing I don't like is that you have to define/register all child classes.

    0 讨论(0)
  • 2020-11-22 04:20

    C++ doesn't allow virtual template member functions right now. The most likely reason is the complexity of implementing it. Rajendra gives good reason why it can't be done right now but it could be possible with reasonable changes of the standard. Especially working out how many instantiations of a templated function actually exist and building up the vtable seems difficult if you consider the place of the virtual function call. Standards people just have a lot of other things to do right now and C++1x is a lot of work for the compiler writers as well.

    When would you need a templated member function? I once came across such a situation where I tried to refactor a hierarchy with a pure virtual base class. It was a poor style for implementing different strategies. I wanted to change the argument of one of the virtual functions to a numeric type and instead of overloading the member function and override every overload in all sub-classes I tried to use virtual template functions (and had to find out they don't exist.)

    0 讨论(0)
提交回复
热议问题