我知道下面的代码是类的部分特化:
template <typename T1, typename T2>
class MyClass {
…
};
// partial specialization: both template parameters have same type
template <typename T>
class MyClass<T,T> {
…
};
另外我知道C ++不允许函数模板部分特化(只允许完整)。但是我的代码是否意味着我对一个/同一类型的参数有部分专业的函数模板?因为它适用于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;
}
答案 0 :(得分:66)
在示例中,您实际上是重载(非专业化) 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 ++标准仅允许 完整 专门化(不包括编译器扩展) ]
答案 1 :(得分:33)
由于不允许部分专业化 - 正如其他答案所指出的那样 - 您可以使用std::is_same
和std::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
虽然这个 all-cases 的东西看起来有点无聊,因为你必须告诉编译器你已经完成的所有事情,最多可以处理5个或更多的特殊化。< / p>
答案 2 :(得分:13)
什么是专业化?
如果你真的想了解模板,你应该看一下函数式语言。 C ++中的模板世界是一个纯粹的功能性子语言。
在函数式语言中,使用模式匹配:
完成选择-- 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 ++类模板的工作方式完全相同。您提供主声明,声明参数的数量和性质。它可以只是一个声明,也可以作为一个定义(你的选择),然后你可以(如果你愿意的话)提供模式的特化,并将它们与它们联系起一个不同的(否则它将是愚蠢的)类的版本
对于模板函数,特化有点尴尬:它与重载解析有些冲突。因此,已经决定专业化将涉及非专业化版本,并且在重载解析期间不会考虑专业化。因此,选择正确函数的算法变为:
(有关深入治疗,请参阅GotW #49)
因此,功能的模板专业化是第二区域公民(字面意思)。就我而言,没有它们我们会更好:我还没有遇到一个模板专业化使用无法通过重载来解决的情况。
这是模板专业吗?
不,这只是一个过载,这很好。实际上,重载通常会像我们期望的那样工作,而专业化可能会令人惊讶(请记住我链接的GotW文章)。
答案 3 :(得分:4)
没有。例如,您可以合法地专门化std::swap
,但您无法合法地定义自己的重载。这意味着您无法使std::swap
适用于您自己的自定义类模板。
在某些情况下,重载和部分特化可以产生相同的效果,但远非所有。
答案 4 :(得分:3)
不允许使用非类,非变量的部分特化,但如上所述:
计算机中的所有问题 科学可以通过解决 另一层次的间接。 - 大卫惠勒
添加一个类来转发函数调用可以解决这个问题,这里有一个例子:
template <class Tag, class R, class... Ts>
struct enable_fun_partial_spec;
struct fun_tag {};
template <class R, class... Ts>
constexpr R fun(Ts&&... ts) {
return enable_fun_partial_spec<fun_tag, R, Ts...>::call(
std::forward<Ts>(ts)...);
}
template <class R, class... Ts>
struct enable_fun_partial_spec<fun_tag, R, Ts...> {
constexpr static R call(Ts&&... ts) { return {0}; }
};
template <class R, class T>
struct enable_fun_partial_spec<fun_tag, R, T, T> {
constexpr static R call(T, T) { return {1}; }
};
template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, int> {
constexpr static R call(int, int) { return {2}; }
};
template <class R>
struct enable_fun_partial_spec<fun_tag, R, int, char> {
constexpr static R call(int, char) { return {3}; }
};
template <class R, class T2>
struct enable_fun_partial_spec<fun_tag, R, char, T2> {
constexpr static R call(char, T2) { return {4}; }
};
static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, "");
static_assert(fun<int>(1, 1) == 2, "");
static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, "");
static_assert(fun<char>(1, 1) == 2, "");
static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, "");
static_assert(fun<long>(1L, 1L) == 1, "");
static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, "");
static_assert(fun<double>(1L, 1L) == 1, "");
static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, "");
static_assert(fun<int>(1u, 1) == 0, "");
static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, "");
static_assert(fun<char>(1, 'c') == 3, "");
static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, "");
static_assert(fun<unsigned>('c', 1) == 4, "");
static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, "");
static_assert(fun<unsigned>(10.0, 1) == 0, "");
static_assert(
std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, "");
static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, "");
static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, "");
static_assert(fun<unsigned>() == 0, "");
答案 5 :(得分:2)
迟到的答案,但是一些迟到的读者可能会发现它很有用:有时,帮助函数 - 设计为可以专门化 - 也可以解决问题。
所以,让我们想象一下,这是我们尝试解决的问题:
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);
}
好的,部分模板功能专业化,我们做不到...所以让我们出口&#34;专门化为辅助函数所需的部分,专门用于帮助函数:
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?
}
这个可以有趣,特别是如果替代方案(正常超载而非专业化,鲁本斯提出的workaround,... - 不是这些是坏的或我的更好,只是另一个一个)将共享相当多的通用代码。