C++接口的模板专用化

C++ template specialization for interface

本文关键字:专用 接口 C++      更新时间:2023-10-16

为什么编译器在运行以下代码时不选择接口模板?是否需要额外的声明/提示,或者这通常不起作用?

我只是好奇这是否真的可能。

class Interface {
    public :
       virtual void Method() = 0;
       virtual ~Interface() { }
};
class Derived : Interface {
    public : 
       void Method() {
            cout<<"Interface method"<<endl;
       }
};
template<typename T> 
struct Selector {
    static void Select(T& o) {
        cout<<"Generic method"<<endl;
    }
};
template<> 
struct Selector<Interface> {
    static void Select(Interface& o) {
        o.Method();
    }
};
int i;
Selector<int>::Select(i)       // prints out "Generic method" -> ok
Derived d;
Selector<Derived>::Select(d);  // prints out "Generic method" -> wrong
                               // should be "Interface method"

试试这个(和#include <type_traits>):

template <typename T, typename = void>
struct Selector
{
    static void Select(T & o)
    {
        std::cout << "Generic method" << std::endl;
    }
};
template <typename T>
struct Selector<T,
           typename std::enable_if<std::is_base_of<Interface, T>::value>::type>
{
    static void Select(Interface & o)
    {
        o.Method();
    }
};

事实证明,enable_if与默认模板参数相结合可用于指导部分专业化。

编译器将选择最匹配的函数版本。采用参数确切类型的函数总是胜过需要转换的函数。在这种情况下,模板函数是完全匹配的,因为它匹配任何内容;Interface专用化需要将参数从Derived转换为Interface

这将使您能够获得所需的结果:

#include <iostream>
#include <type_traits>
using namespace std;
class Interface {
    public :
       virtual void Method() = 0;
       virtual ~Interface() { }
};
class Derived : public Interface {
    public : 
       void Method() {
            cout<<"Interface method"<<endl;
       }
};
template<typename T, typename S = void> 
struct Selector {
    static void Select(T& o) {
        cout<<"Generic method"<<endl;
    }
};
template<typename T>
struct Selector<T, typename enable_if< is_base_of<Interface, T>::value >::type> {
    static void Select(Interface& o) {
        o.Method();
    }
};
int main()
{
int i;
Selector<int>::Select(i);       // prints out "Generic method" -> ok
Derived d;
Selector<Derived>::Select(d);  // prints out "Generic method" -> wrong
                               // should be "Interface method"
}