更一般地说,一般来说,模板和继承是一个长期存在的问题。
问题是模板在精确类型上工作并且不考虑继承因素,这两个概念有些正交,因此尝试混合一个和另一个通常容易出错。
您也可以使用以下方法进行检查:
template <class T>
int fooBar(T) { return 10; }
int fooBar(A) { return 20; }
B b;
fooBar(b); // this returns 10, because fooBar<T> is a better match (no conversion)
现在,关于你的问题,虽然我很欣赏已经给出的各种解决方案enable_if
和is_base_of
技巧,但我放弃了它们,因为它们不实用。专业化的重点是,Foo
如果有必要,作者不必知道任何人将如何专业化她的课程,只是为了让它变得容易。否则,如果你需要十几个专业,你最终会得到一个非常非常奇怪Foo
的课程,这是肯定的。
STL 已经处理过类似的问题。公认的习惯用法通常是提供一个特征类。默认的特征类为每个人提供了一个很好的解决方案,而人们可以专门化一个特征类来满足自己的需要。
我认为应该有一种使用概念的方法(即,如果 T 定义了 T::fooBar() 然后使用它,否则使用默认版本......),但对于特定的方法重载,这不是必需的。
namespace detail { int fooBar(...) { return 10; } }
template <class T>
class Foo
{
public:
static int FooBar() { T* t(0); return ::detail::fooBar(t); }
};
现在,专门针对 A 的派生类:
namespace detail { int fooBar(A*) { return 20; } }
它是如何工作的?在考虑重载时,省略号是最后考虑的方法,因此之前符合条件的任何方法都可以,因此它非常适合默认行为。
一些考虑:
这是一个带有模板函数的示例
namespace detail { template <class T> int fooBar(...) { return 10; } }
template <class T>
int Foo<T>::FooBar() { T* t(0); return ::detail::fooBar<T>(t); }
namespace detail {
template <class T>
int fooBar(A*)
{
return T::FooBar();
}
}
这里会发生什么:
struct None {};
struct A { static int FooBar() { return 20; } };
struct B: A {};
struct C: A { static int FooBar() { return 30; } };
int main(int argc, char* argv[])
{
std::cout << Foo<None>::FooBar() // prints 10
<< " " << Foo<A>::FooBar() // prints 20
<< " " << Foo<B>::FooBar() // prints 20
<< " " << Foo<C>::FooBar() // prints 30
<< std::endl;
}