Template specialization and derived classes in C++

后端 未结 1 1246
面向向阳花
面向向阳花 2021-02-19 06:43

I have this simple code:

class A{};
class B : public A{};
class C : public B{};

class Test
{
    public:
        template
        void f(T&         


        
相关标签:
1条回答
  • 2021-02-19 07:14

    Yes, it is possible, but you have to change your code a bit.

    First of all, to be technical, the second function f() is not a specialization of the template function, but an overload. When resolving overload, the template version is chosen for all arguments whose type is not A, because it is a perfect match: T is deduced to be equal to the type of the argument, so when calling f(b), for instance, after type deduction the compiler will have to choose between the following two overloads:

    void f(B&){printf("template\n");}
    void f(A&){printf("specialization\n");}
    

    Of course, the first one is a better match.

    Now if you want the second version to be selected when the function is invoked with an argument which is a subclass of A, you have to use some SFINAE technique to prevent the function template from being correctly instantiated when the type T is deduced to be a subclass of A.

    You can use std::enable_if in combination with the std::is_base_of type traits to achieve that.

    // This will get instantiated only for those T which are not derived from A
    template<typename T,
        typename enable_if<
            !is_base_of<A, T>::value
            >::type* = nullptr
        >
    void f(T&) { cout << "template" << endl; }
    

    Here is how you would use it in a complete program:

    #include <type_traits>
    #include <iostream>
    
    using namespace std;
    
    class A{};
    class B : public A{};
    class C : public B{};
    class D {};
    
    class Test
    {
        public:
    
            template<typename T,
                typename enable_if<!is_base_of<A, T>::value>::type* = nullptr
                >
            void f(T&) { cout << ("template\n"); }
    
            void f(A&){ cout << ("non-template\n");}
    
    };
    
    int main()
    {
        A a;
        B b;
        C c;
        D d;
        float f;
    
        Test test;
        test.f(a); // Will print "non-template"
        test.f(b); // Will print "non-template"
        test.f(c); // Will print "non-template"
        test.f(d); // Will print "template"
        test.f(f); // Will print "template"
    }
    

    EDIT:

    If you are working with a compiler which is not fully compliant with C++11 (and therefore does not support default template arguments on function templates), you might want to change the definition of your template overload of f() as follows:

    template<typename T>
    typename enable_if<!is_base_of<A, T>::value, void>::type 
    f(T&) { cout << ("template\n"); }
    

    The behavior of the program will be identical. Note that if the return type of f() is void, you can omit the second argument to the enable_if class template.

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