C ++函数模板部分专业化?

我知道下面的代码是一个类的部分专业化:

template <typename T1, typename T2> class MyClass { … }; // partial specialization: both template parameters have same type template <typename T> class MyClass<T,T> { … }; 

另外我知道C ++不允许函数模板局部特化(只允许满)。 但是,我的代码意味着我已经部分专门化我的函数模板为一个/相同types的参数? 因为它适用于Microsoft Visual Studio 2010 Express! 如果不是,那么你能否解释一下部分专业化的概念?

 #include <iostream> using std::cin; using std::cout; using std::endl; template <typename T1, typename T2> inline T1 max (T1 const& a, T2 const& b) { return a < b ? b : a; } template <typename T> inline T const& max (T const& a, T const& b) { return 10; } int main () { cout << max(4,4.2) << endl;; cout << max(5,5) << endl; int z; cin>>z; } 

在这个例子中,实际上是重载(而不是专门) max<T1,T2>函数。 部分专业化语法应该看起来像下面( 如果允许 ):

 //Partial specialization is not allowed by the spec, though! template <typename T> inline T const& max<T,T> (T const& a, T const& b) { ^^^^^ <--- specializing here return 10; } 

[注意:在函数模板的情况下,C ++标准只允许完全专门化(不包括编译器扩展)]。

由于不允许部分特化 – 正如其他答案指出的那样 – 你可以使用std::is_samestd::enable_if来解决它,如下所示:

 template <typename T, class F> inline typename std::enable_if<std::is_same<T, int>::value, void>::type typed_foo(const F& f) { std::cout << ">>> messing with ints! " << f << std::endl; } template <typename T, class F> inline typename std::enable_if<std::is_same<T, float>::value, void>::type typed_foo(const F& f) { std::cout << ">>> messing with floats! " << f << std::endl; } int main(int argc, char *argv[]) { typed_foo<int>("works"); typed_foo<float>(2); } 

输出:

 $ ./a.out >>> messing with ints! works >>> messing with floats! 2 

编辑 :如果你需要能够处理剩下的所有其他案例,你可以添加一个定义,说明已经处理的案例不应该匹配 – 否则你会陷入模棱两可的定义。 定义可以是:

 template <typename T, class F> inline typename std::enable_if<(not std::is_same<T, int>::value) and (not std::is_same<T, float>::value), void>::type typed_foo(const F& f) { std::cout << ">>> messing with unknown stuff! " << f << std::endl; } int main(int argc, char *argv[]) { typed_foo<int>("works"); typed_foo<float>(2); typed_foo<std::string>("either"); } 

其中产生:

 $ ./a.out >>> messing with ints! works >>> messing with floats! 2 >>> messing with unknown stuff! either 

尽pipe这种情况看起来有些乏味,但是因为你必须告诉编译器你已经完成的所有事情,所以处理多达5个或者更多的专门化是相当可行的。

什么是专业化?

如果你真的想了解模板,你应该看看function语言。 C ++中的模板世界是一个纯粹的function子语言。

在函数式语言中,使用模式匹配来完成select:

 -- An instance of Maybe is either nothing (None) or something (Just a) -- where a is any type data Maybe a = None | Just a -- declare function isJust, which takes a Maybe -- and checks whether it's None or Just isJust :: Maybe a -> Bool -- definition: two cases (_ is a wildcard) isJust None = False isJust Just _ = True 

正如你所看到的,我们超载isJust的定义。

那么,C ++类模板的工作方式完全相同。 你提供一个主要的声明,说明参数的数量和性质。 它可以只是一个声明,或者也可以作为一个定义(你的select),然后你可以(如果你愿意的话)提供这个模式的特化,并把它们联系到一个不同的类别(否则这将是愚蠢的) 。

对于模板函数来说,专业化比较笨拙,它与重载parsing有些冲突。 因此,已经决定专业化将涉及到一个非专业化的版本,在重载决议期间不会考虑专业化。 因此,select正确function的algorithm变成:

  1. 在常规function和非专用模板中执行重载parsing
  2. 如果select了一个非专门化的模板,检查是否存在专门化,这将是一个更好的匹配

(深入治疗,请参阅GotW#49 )

因此,function的模板专业化是二区公民(字面上)。 就我而言,如果没有他们,我们会更好:我还没有遇到一个模板专业化使用不能用重载来解决的情况。

这是模板专业吗?

不,这只是一个过载,这是好的。 实际上,重载通常会按照我们预期的那样工作,而专业化可能会令人惊讶(请记住我链接的GotW文章)。

不可以。例如,您可以合法地使用std::swap ,但是您无法合法定义自己的超载。 这意味着你不能让你的自定义类模板的std::swap工作。

重载和部分专业化在某些情况下可能具有相同的效果,但远非如此。

迟到的答案,但一些迟到的读者可能会觉得它有用:有时候,一个辅助function – devise成可以专业化的 – 也可以解决这个问题。

那么让我们来想象一下,这就是我们试图解决的问题:

 template <typename R, typename X, typename Y> void function(X x, Y y) { R* r = new R(x); f(r, y); // another template function? } // for some reason, we NEED the specialization: template <typename R, typename Y> void function<R, int, Y>(int x, Y y) { // unfortunately, Wrapper has no constructor accepting int: Wrapper* w = new Wrapper(); w->setValue(x); f(w, y); } 

好吧,部分模板函数专业化,我们不能这样做…所以让我们将专业化所需的部分“导出”到一个辅助函数中,专门化这个函数并使用它:

 template <typename R, typename T> R* create(T t) { return new R(t); } template <> Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal... { Wrapper* w = new Wrapper(); w->setValue(n); return w; } template <typename R, typename X, typename Y> void function(X x, Y y) { R* r = create<R>(x); f(r, y); // another template function? } 

可能是有趣的,尤其是如果替代scheme(正常的重载,而不是专业化,鲁本斯提出的解决方法 , – 不是这些是坏的或我的更好,只是另一个 )将分享相当多的通用代码。