我读了 C++ Primer,它说函数模板专业化是一个高级主题,但我完全迷失了。谁能提供一个例子,为什么功能模板专业化很重要和必要?
为什么函数模板不支持偏特化,而类模板支持?背后的逻辑是什么?
我读了 C++ Primer,它说函数模板专业化是一个高级主题,但我完全迷失了。谁能提供一个例子,为什么功能模板专业化很重要和必要?
为什么函数模板不支持偏特化,而类模板支持?背后的逻辑是什么?
您可以在此处回答为什么函数不支持部分专业化的问题。下面的代码显示了如何实现不同的专业化。
template<typename T>
bool Less(T a, T b)
{
cout << "version 1 ";
return a < b;
}
// Function templates can't be partially specialized they can overload instead.
template<typename T>
bool Less(T* a, T* b)
{
cout << "version 2 ";
return *a < *b;
}
template<>
bool Less<>(const char* lhs, const char* rhs)
{
cout << "version 3 ";
return strcmp(lhs, rhs) < 0;
}
int a = 5, b = 6;
cout << Less<int>(a, b) << endl;
cout << Less<int>(&a, &b) << endl;
cout << Less("abc", "def") << endl;
我想不出一个例子,自从你问起我就一直在尝试。正如Jagannath所指出的,长期以来一直建议不要专门化函数,而是重载它们或使用特征类(可以是专门化的,甚至是部分专门化的)。
例如,如果您需要交换两个项目,那么依赖重载会更好(更可预测且更可扩展):
template<class T>
void f() {
T a, b;
using std::swap; // brings std::swap into scope as "fallback"
swap(a, b); // unqualified call (no "std::") so ADL kicks in
// also look at boost::swap
}
以及如何为您的类型编写交换:
// the cleanest way to do it for a class template:
template<class T>
struct Ex1 {
friend void swap(Ex1& a, Ex1& b) { /* do stuff */ }
};
// you can certainly place it outside of the class instead---but in the
// same namespace as the class---if you have some coding convention
// against friends (which is common, but misguided, IMHO):
struct Ex2 {};
void swap(Ex2& a, Ex2& b) { /* do stuff */ }
两者都允许参数相关查找(ADL)。
其他函数,例如 stringify/str 或 repr(表示)同样可以是非成员,并通过重载利用 ADL:
struct Ex3 {
friend std::string repr(Ex3 const&) { return "<Ex3 obj>"; }
};
std::string repr(bool b) { return b ? "true" : "false"; }
// possible fallback:
template<class T>
std::string repr(T const& v) {
std::ostringstream out;
out << v;
return out.str();
}
// but in this particular case, I'd remove the fallback and document that
// repr() must be overloaded appropriately before it can be used with a
// particular type; for other operations a default fallback makes sense
换个角度来看,如果函数模板可以用作特定实现的注册表会很好,但是由于限制(在当前的 C++ 中,不确定 C++0x 到底带来了什么),它们不能正常工作作为用于该注册表目的的重载或类模板。
有一个方便但不重要的用途:轻松将某些特化定义到单独的库中,可能是共享库(.so 或 .dll)。这很方便,因为它需要对通用模板进行最少的更改,但并不重要,因为它对我来说似乎很少见(在野外,而且在我的经验中当然很少见)并且实现者仍然可以使用重载或转发到完全专业化的类模板的非专门方法。
为了说明为什么函数模板特化很重要,请考虑std::swap
模板函数。默认情况下,std::swap(x, y)
基本上是:
T temp = x;
x = y;
y = temp;
但这可能效率低下,因为它涉及创建一个额外的副本,x
并且可以在分配中进行额外的复制。x
如果它很大(例如,如果它std::vector
有很多元素) ,这尤其糟糕。此外,上述每一行都可能失败并引发异常,可能会导致x
处于y
不良、不一致的状态。
为了解决这个问题,许多类提供了它们自己的swap
方法(包括std::vector
)来代替交换指向它们内部数据的指针。这样效率更高,并且可以保证永远不会失败。
但是现在您有一个案例,您可以std::swap(x, y)
在某些类型上使用但需要x.swap(y)
在其他类型上调用。这很令人困惑,而且对模板不利,因为它们无法以通用、一致的方式交换两个对象。
但std::swap
可以专门化,以便x.swap(y)
在特定类型上调用时调用。这意味着您可以std::swap
在任何地方使用并(希望)期望它表现良好。
基本上,这个想法是您可以编写针对一般情况以通用方式运行的模板,但仍可以处理特殊情况。使用专业化的一个例子是在std::vector
. std::vector<bool>
是一种封装bool
元素的特化,使得它们每个元素只使用一个位,而不是一个字节。 std::vector<T>
像所有其他类型的普通动态数组一样工作。
专业化的更高级用途是元编程。例如,这里有一个示例(来自 Wikipedia),说明如何在编译时使用模板专业化来计算阶乘。
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};