模板特化与局限性

内容简介

模板能表达通用逻辑,但不是所有类型都天然支持同一套操作。遇到特定类型需要特殊处理时,可以使用重载、特化或 concepts。

模板的局限性

这个模板要求 T 支持赋值:

template <typename T>
void copy_value(T& a, const T& b)
{
    a = b;
}

如果传入不支持赋值的类型,就无法编译。

这个模板要求 T 支持 >

template <typename T>
T max_value(T a, T b)
{
    return a > b ? a : b;
}

如果 T 是自定义类型,并且没有定义比较逻辑,也无法编译。

函数模板特化

普通模板:

template <typename T>
bool equal(T a, T b)
{
    return a == b;
}

对 C 风格字符串特殊处理:

template <>
bool equal<const char*>(const char* a, const char* b)
{
    return std::strcmp(a, b) == 0;
}

原因:const char*== 比较的是地址,不是字符串内容。

类模板特化

template <typename T>
class Printer {
public:
    void print(const T& value)
    {
        std::cout << value << '\n';
    }
};
 
template <>
class Printer<const char*> {
public:
    void print(const char* value)
    {
        std::cout << (value ? value : "(null)") << '\n';
    }
};

偏特化

类模板支持偏特化,函数模板不支持偏特化。

template <typename T>
class Storage {
};
 
template <typename T>
class Storage<T*> {
    // 专门处理指针类型
};

现代替代:concepts

C++20 可以用 concepts 直接写出模板要求:

template <typename T>
concept Addable = requires(T a, T b) {
    a + b;
};
 
template <Addable T>
T add(T a, T b)
{
    return a + b;
}

最佳代码实践

  • 简单差异优先用函数重载。
  • 类型族差异可以考虑类模板偏特化。
  • C++20 后复杂约束优先用 concepts。
  • 不要用特化掩盖设计问题,特化越多,模板越难维护。

错误用法

template <typename T>
bool less_than(T a, T b)
{
    return a < b;  // 假设所有类型都支持 <
}

问题:模板没有说明类型要求,错误会在实例化时暴露,报错可能很长。

注意事项

  • 模板的“通用”建立在类型满足操作要求的前提上。
  • 特化是给特定类型开分支,不是普通 if。
  • 泛型代码越底层,越需要把类型要求写清楚。