开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情
C++ 模板技巧是 C++ 编程的一个重要部分,可以使程序员能够创建可重用的代码。模板是 C++ 中的一种高级特性,允许您在定义函数或类时使用类型参数,这些类型参数在编译时由编译器替换为具体的类型。下面是一些有关 C++ 模板的常见技巧的总结。
- 模板类型参数化
C++ 模板允许您在定义函数或类时指定类型参数。这些类型参数在编译时由编译器替换为具体的类型。例如,下面是一个模板函数,该函数可以接受两个参数,并返回这两个参数的最大值:
template <typename T>
T max(T a, T b)
{
return (a > b) ? a : b;
}
在这里,类型参数 T 被用作函数的参数类型和返回类型。当调用这个函数时,必须指定具体的类型来代替 T,例如:
int m = max(3, 4); // m = 4
double d = max(3.14, 2.71); // d = 3.14
在上面的例子中,编译器会分别为 T 生成两个版本的代码,一个版本用于 int 类型,另一个版本用于 double 类型。
- 多个类型参数
可以使用多个类型参数来定义模板。例如,下面是一个模板类,该类可以按照指定的顺序将两个参数交换:
template <typename T1, typename T2>
class Pair
{
public:
Pair(T1 a, T2 b) : m_a(a), m_b(b) {}
void swap();
private:
T1 m_a;
T2 m_b;
};
template <typename T1, typename T2>
void Pair<T1, T2>::swap()
{
T1 temp = m_a;
m_a = m_b;
m_b = temp;
}
在上面的例子中,编译器会生成一个特定于 int 和 double 类型的版本的代码。
- 默认类型参数
可以为类型参数指定默认值,以便在调用模板时可以省略某些类型参数。例如,下面是一个带有默认类型参数的模板函数,该函数可以求两个数的和:
template <typename T1=int, typename T2=int>
T1 add(T1 a, T2 b)
{
return a + b;
}
在这里,类型参数 T1 和 T2 分别用作两个参数的类型,并且都被设置为默认值 int。因此,可以省略这些类型参数,例如:
int s1 = add(3, 4); // s1 = 7
double s2 = add(3.14, 2.71); // s2 = 5.85
- 函数模板的重载
函数模板可以被重载。例如,可以定义一个求两个数的最大值的函数模板,并为其提供另一个版本,用于求三个数的最大值。例如:
template <typename T>
T max(T a, T b)
{
return (a > b) ? a : b;
}
template <typename T>
T max(T a, T b, T c)
{
return max(max(a, b), c);
}
在这里,第一个版本的函数模板接受两个参数,第二个版本接受三个参数。当调用这个函数时,编译器会根据参数的个数自动选择适当的版本。
- 模板特化
可以为模板定义特殊的版本,以便在某些特定情况下使用。这种特殊版本称为“模板特化”。例如,可以为函数模板 max 定义一个特化版本,用于计算三个 char 类型参数的最大值:
template <typename T>
T max(T a, T b, T c)
{
return max(max(a, b), c);
}
template <>
char max(char a, char b, char c)
{
char result = a;
if (b > result) result = b;
if (c > result) result = c;
return result;
}
在这里,特化版本的函数模板只能用于计算三个 char 类型参数的最大值。当调用这个函数时,如果传递的参数是 char 类型,则编译器会使用特化版本;如果参数的类型不是 char,则编译器会使用默认版本。
- 模板的偏特化
模板也可以进行“偏特化”,即只特化某些类型参数,而其他类型参数仍然是泛型。例如,可以为函数模板 max 定义一个偏特化版本,用于计算两个指针类型参数的最大值:
template <typename T1, typename T2>
T1 max(T1 a, T2 b)
{
return (a > b) ? a : b;
}
template <typename T>
T* max(T* a, T* b)
{
return (*a > *b) ? a : b;
}
在这里,偏特化版本的函数模板只能用于计算两个指针类型参数的最大值。当调用这个函数时,如果传递的参数是指针类型,则编译器会使用偏特化版本;如果参数的类型不是指针,则编译器会使用默认版。
- 模板的友元函数
模板函数可以声明为类的友元函数。这样,就可以在模板函数内访问类的私有成员。例如,可以在模板类 Pair 中声明一个友元函数,用于交换两个对象的值:
template <typename T1, typename T2>
class Pair
{
public:
Pair(T1 a, T2 b) : m_a(a), m_b(b) {}
friend void swap(Pair& p1, Pair& p2);
private:
T1 m_a;
T2 m_b;
};
template <typename T1, typename T2>
void swap(Pair<T1, T2>& p1, Pair<T1, T2>& p2)
{
T1 temp = p1.m_a;
p1.m_a = p2.m_a;
p2.m_a = temp;
T2 temp2 = p1.m_b;
p1.m_b = p2.m_b;
p2.m_b = temp2;
}
在这里,函数模板 swap 被声明为 Pair 类的友元函数。这样,就可以在函数模板内访问 Pair 类的私有成员。
- 模板的模板参数
模板还可以有模板参数。例如,可以定义一个模板函数,该函数接受一个容器类型作为参数,并将容器中的所有元素打印出来。下面是一个示例:
template <template <typename, typename> class Container, typename Value, typename Allocator>
void printContainer(const Container<Value, Allocator>& c)
{
for (auto& v : c)
{
std::cout << v << " ";
}
std::cout << std::endl;
}