C++模版类与模版函数

C++模版类


1. 模板简介

C++模板是泛型编程的核心,允许编写与类型无关的代码。通过模板,可以定义函数模板和类模板,支持多种数据类型的操作。例如:

template
T add(T a, T b) { return a + b; }

template
class Container {
    T data;
    // ... 成员函数
};

2. 完全特例化(Full Specialization)

定义:为模板的所有参数提供特定类型的实现,覆盖通用模板的行为。

语法

template<>
class/struct 模板名<具体类型列表> { ... };

示例

template
class Printer {
public:
    void print(const T& val) { std::cout << "通用版本: " << val << std::endl; }
};

// 完全特例化int版本
template<>
class Printer {
public:
    void print(int val) { std::cout << "int特例化: " << val << std::endl; }
};

使用场景:当某个具体类型需要特殊处理时(如优化性能、避免通用实现的问题)。


3. 部分特例化(Partial Specialization,非完全特例化)

定义:针对模板参数的部分组合或特性(如指针、引用、模板嵌套等)提供特化版本。

语法

template<模板参数列表>
class/struct 模板名<特化的参数模式> { ... };

示例

template
class Printer {  // 部分特例化指针类型
public:
    void print(T* ptr) { std::cout << "指针版本: " << *ptr << std::endl; }
};

// 部分特例化两个类型相同的场景
template
class Pair { ... };

template
class Pair { ... };  // 当两个类型相同时使用此版本

与完全特例化的区别

  • 完全特例化:所有模板参数均已确定。
  • 部分特例化:仅部分参数确定,或基于参数模式(如T*)。

注意:函数模板不支持部分特例化,但可通过重载模拟类似行为。


4. 类型萃取(Traits)

概念:一种通过模板特例化获取类型信息或转换类型的技术,常用于编译期类型计算。

实现方式:通过完全特例化或部分特例化定义类型属性。

示例1:判断是否为指针

template
struct is_pointer {
    static const bool value = false;
};

template
struct is_pointer {  // 部分特例化指针类型
    static const bool value = true;
};

// 使用
bool isIntPtr = is_pointer::value;  // true

示例2:移除const修饰符

template
struct remove_const {
    using type = T;
};

template
struct remove_const {  // 部分特例化const T
    using type = T;
};

// 使用
remove_const::type x = 5;  // x的类型为int

应用场景

  • STL迭代器std::iterator_traits 萃取迭代器的值类型、指针类型等。
  • 条件编译:结合std::enable_if实现SFINAE(替换失败不是错误)。
  • 优化算法:根据类型特性选择不同实现(如对std::is_integral类型优化)。

5. 综合示例:类型萃取与特例化结合
#include 

// 类型萃取:判断是否为浮点类型
template
struct is_floating_point {
    static const bool value = false;
};

template<>
struct is_floating_point {
    static const bool value = true;
};

template<>
struct is_floating_point {
    static const bool value = true;
};

// 根据类型选择打印策略
template::value>
class NumberPrinter;

// 浮点类型的特例化版本
template
class NumberPrinter {
public:
    void print(T val) {
        std::cout << "浮点数: " << val << std::endl;
    }
};

// 非浮点类型的通用版本
template
class NumberPrinter {
public:
    void print(T val) {
        std::cout << "整数: " << val << std::endl;
    }
};

int main() {
    NumberPrinter().print(42);      // 输出:整数: 42
    NumberPrinter().print(3.14); // 输出:浮点数: 3.14
}

6. 总结
  • 完全特例化:为所有模板参数提供具体类型的实现。
  • 部分特例化:基于模板参数的模式或部分参数进行特化,仅适用于类模板。
  • 类型萃取:利用特例化技术提取或转换类型信息,广泛用于编译期逻辑判断和优化。

通过灵活使用这些技术,可以编写出高效、可维护的泛型代码,充分体现C++模板编程的强大能力。


模版函数


1. 函数模板基础

定义:函数模板允许编写与类型无关的通用函数,支持多种参数类型。

template
T max(T a, T b) {
    return (a > b) ? a : b;
}

// 使用
int x = max(3, 5);          // T推导为int
double y = max(2.5, 3.14);  // T推导为double

核心特性

  • 类型推导:编译器根据传入参数自动推导模板参数类型。

  • 显式指定类型:可强制指定模板参数:

    int z = max(3, 5.5); // T显式指定为double
    

2. 函数模板的特化
完全特例化(Full Specialization)

作用:为特定类型提供专用实现。

template<>
const char* max(const char* a, const char* b) {
    return (strcmp(a, b) > 0) ? a : b;
}

// 使用
const char* s = max("apple", "banana"); // 调用特例化版本

限制

  • 必须显式指定所有模板参数。
  • 函数模板不支持部分特例化(语法不允许)。
通过重载模拟部分特例化

虽然不能部分特例化函数模板,但可以通过重载实现类似效果:

// 通用版本
template
void process(T val) { 
    std::cout << "通用类型处理\n"; 
}

// 重载指针版本(模拟部分特例化)
template
void process(T* ptr) { 
    std::cout << "指针类型处理\n"; 
}

// 使用
int x = 5;
process(x);    // 调用通用版本
process(&x);   // 调用指针重载版本

3. 函数模板与类模板协作
结合类型萃取
#include 

template
void printValue(T val) {
    if constexpr (std::is_pointer_v) {  // C++17编译期条件判断
        std::cout << "指针指向的值: " << *val << "\n";
    } else {
        std::cout << "普通值: " << val << "\n";
    }
}

// 使用
int a = 10;
printValue(a);    // 输出:普通值: 10
printValue(&a);   // 输出:指针指向的值: 10

4. 函数模板重载规则

当多个重载版本匹配时,编译器优先级:

  1. 非模板函数 > 模板特化 > 通用模板
  2. 更具体的模板优先于更通用的模板

示例

template
void log(T val) { std::cout << "通用模板\n"; }

template
void log(T* val) { std::cout << "指针模板\n"; }  // 更具体的重载

void log(int val) { std::cout << "非模板int版本\n"; }  // 非模板函数

// 使用
int x = 5;
log(x);    // 调用非模板int版本
log(&x);   // 调用指针模板
log(3.14); // 调用通用模板

5. 高级应用:SFINAE与enable_if

通过模板元编程控制函数模板的有效性:

#include 

// 仅允许整数类型调用
template
typename std::enable_if_t, void>
handleNumber(T num) {
    std::cout << "处理整数: " << num << "\n";
}

// 仅允许浮点类型调用
template
typename std::enable_if_t, void>
handleNumber(T num) {
    std::cout << "处理浮点数: " << num << "\n";
}

// 使用
handleNumber(42);     // 输出:处理整数: 42
handleNumber(3.14);   // 输出:处理浮点数: 3.14
// handleNumber("test"); // 编译错误:无匹配函数

6. 函数模板与类模板对比
特性 函数模板 类模板
特化方式 支持完全特例化 支持完全特例化和部分特例化
参数推导 支持自动类型推导(无需<> 必须显式指定或通过构造函数推导(C++17起)
重载 可直接重载 通过成员函数重载
部分特例化 不支持(通过重载模拟) 支持
典型应用场景 通用算法(如排序、数学运算) 容器(如vector)、工具类

7. 综合示例:通用序列化函数
#include 
#include 
#include 

// 基础类型序列化
template
void serialize(T val) {
    if constexpr (std::is_arithmetic_v) {
        std::cout << "序列化数值: " << val << "\n";
    } else {
        static_assert(std::is_arithmetic_v, "仅支持基础类型!");
    }
}

// 指针特化版本
template
void serialize(T* ptr) {
    if (ptr != nullptr) {
        std::cout << "序列化指针: ";
        serialize(*ptr);  // 递归调用基础版本
    }
}

// 容器特化版本(需C++20概念支持,此处简化)
template
void serialize(const std::vector& vec) {
    std::cout << "序列化容器[ ";
    for (const auto& item : vec) {
        serialize(item);  // 递归调用
    }
    std::cout << "]\n";
}

// 使用
int main() {
    serialize(42);                         // 基础类型
    double d = 3.14;
    serialize(&d);                        // 指针
    std::vector vec = {1, 2, 3};
    serialize(vec);                       // 容器
}

总结

  • 函数模板是实现通用算法的核心工具,通过类型推导和重载机制提供灵活性。
  • 完全特例化用于为特定类型定制行为,但需注意与重载的优先级关系。
  • SFINAEif constexpr(C++17)可实现编译期条件分支,增强模板的泛用性。
  • 结合类模板和类型萃取技术,可以构建强大的类型敏感逻辑系统。

你可能感兴趣的:(C++,c++,开发语言)