功能模板专业化的重要性和必要性

时间:2010-02-04 03:29:54

标签: c++ template-specialization

我读过C ++ Primer,它说功能模板专业化是一个高级主题,但我完全迷失了。任何人都能举例说明为什么功能模板专业化很重要且必要吗?

为什么功能模板在类模板时不支持部分特化?什么是基本逻辑?

4 个答案:

答案 0 :(得分:8)

关于为什么函数不支持部分特化的问题可以回答here。下面的代码显示了如何实现不同的专业化。

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;

答案 1 :(得分:3)

我想不出一个例子,自从你问过以后,我几乎一直在努力。正如Jagannath所指出的那样,long-standing advice没有专门化函数,而是重载它们或使用特征类(可以专门化,甚至部分专门化)。< / p>

例如,如果您需要交换两个项目,那么依赖重载会更好(更可预测且更具可扩展性):

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 */ }

两者都允许Argument Dependent Lookup(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)。这很方便,因为它只需要对通用模板进行最少的更改,但并不重要,因为它对我来说似乎很少(在我看来很少见,而且在我的经验中确实很少见),实现者仍然可以使用重载或转发到完全专用的类模板的非专业化方法。

答案 2 :(得分:1)

为了说明函数模板特化的重要性,请考虑std::swap模板函数。默认情况下,std::swap(x, y)基本上执行:

T temp = x;
x = y;
y = temp;

但这可能效率低下,因为它涉及创建x的额外副本,并且可以在分配中进行额外的复制。如果x很大(例如,如果它是具有许多元素的std::vector),则这尤其糟糕。此外,上述每一行都可能失败并抛出异常,可能会使xy处于错误的不一致状态。

为了解决这个问题,许多类提供了自己的swap方法(包括std::vector),而是将指针交换到它们的内部数据。这样效率更高,可以保证永不失败。

但是现在你有一个案例,你可以在某些类型上使用std::swap(x, y),但需要在其他类型上调用x.swap(y)。这是令人困惑的,这对模板来说很糟糕,因为它们无法以通用,一致的方式交换两个对象。

std::swap可以专门,以便在调用特定类型时调用x.swap(y)。这意味着您可以在任何地方使用std::swap,并且(希望)期望它表现得很好。

答案 3 :(得分:0)

基本上,我们的想法是你可以编写一般情况下以通用方式运行的模板,但仍然可以处理特殊情况。使用专业化的一个例子是std::vectorstd::vector<bool>是一种特殊化,它包含bool元素,使得它们每个元素只使用一位,而不是一个字节。 std::vector<T>的工作方式类似于所有其他类型的普通动态数组。

专业化的更高级用途是元编程。例如,这是一个示例(来自维基百科),它介绍了如何在编译时使用模板特化来计算阶乘。

template <int N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};