C++函数模板随笔总结

自动推导类型 auto

auto关键字是C++11中的用法。在C++11之前,auto被用作声明自动存储局部变量。

最基本的,在C++11中,我们可以使用auto关键字让编译器推导变量的类型,就像下面这个例子:

auto i = 42; // i被推导为int
auto d = 42.0; // d被推导为double
auto s = "hello"; // s被推导为const char*

编译器会根据初始化的值自动推导变量的类型。

然后,你需要注意的有:

  1. auto声明的变量在定义时必须初始化。
  2. auto不能作为函数的形参类型。
  3. auto不能直接声明数组。
  4. auto不能定义类的非静态成员变量。

然而,auto关键字一下情况中非常好用:

  1. 当变量类型很长或者复杂时,使用auto可以让代码更加简洁。
  2. 当处理模板时,使用auto可以简化依赖模板参数的变量声明。
  3. 函数模板依赖模板参数的返回值。
  4. 使用lambda表达式时,auto可以用于声明函数类型。

例如,假设我们有一个很长的类型,如std::unordered_map<std::string, std::vector<int>>,使用auto可以让我们的代码更简洁:

std::unordered_map<std::string, std::vector<int>> map;
// 无auto
for (std::unordered_map<std::string, std::vector<int>>::iterator it = map.begin(); it != map.end(); ++it) {
    // ...
}
// 使用auto
for (auto it = map.begin(); it != map.end(); ++it) {
    // ...
}

函数模板

函数模板是C++中用于实现泛型编程的重要工具,它可以创建通用函数,处理不同的数据类型,但其基本操作相同。这样可以提高代码的复用性和灵活性。

简单的,一个基本的函数模板看起来像这样:

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

其中template<typename T>是模板声明,它告诉编译器我们将创建一个模板,并且我们将使用一个名为T的类型参数。类型参数可以是任何有效地数据类型(如int , double , string 等等)。T只是一个占位符,你可以用任何有效地标识符替换它。

在函数中,我们看到T被用作变量abtmp的类型。这意味着这些变量可以接受任何类型,只要该类型支持赋值操作。

现在,我们可以使用这个函数模板来交换任何类型的两个变量,只要这两个变量的类型相同,并且该类型支持赋值操作。例如:

int x = 1, y = 2;
Swap(x, y); // 现在x=2, y=1

double m = 1.1, n = 2.2;
Swap(m, n); // 现在m=2.2, n=1.1

std::string s1 = "Hello", s2 = "World";
Swap(s1, s2); // 现在s1="World", s2="Hello"

注意点:

  1. 函数模板可以有多个类型参数。例如:template <typename T, typename U> void foo(T t, U u) {...}
  2. 不是所有类型都可以用于函数模板。类型必须支持函数中使用的所有操作。例如,如果函数模板使用了除法操作,那么类型参数就必须支持除法操作。
  3. 模板类型参数可以显式指定,例如Swap<int>(x, y);,但通常让编译器自动推导是更好的选择。
  4. 可以为类的成员函数创建模板,但不能是虚函数和析构函数。
  5. 函数模板支持重载,可以有非通用数据类型的参数

函数模板具体化

函数模板具体化(特化)是一个特殊的函数模板,其可以为特定的类型提供特别处理的实现。在编译期,编译器会优先选择具体化的版本,而不是通用的模板版本。

下面是具体的例子展示:

template <typename T>
void print(T val) {
    std::cout << "General template: " << val << std::endl;
}

template <>
void print<int>(int val) {
    std::cout << "Specialized template for int: " << val << std::endl;
}

int main() {
    print("Hello");  // 使用通用模板
    print(10);       // 使用特化模板
}

在上述例子中,我们创建了一个通用的print函数模板,用于打印各种类型的值,同时也为int类型提供了一个特化的print函数模板,当我们调用print(10)时,编译器会优先选择具体化模板,因为它为int类型提供了更准确的匹配。

那么,怎么使用这种特性才能最大化其效果呢?在编译过程中,有时候你可能会遇到需要对某种类型做特殊化处理的情况,这时候就可以利用函数模板具体化。但是需要注意,具体化版本应该仅针对那些无法通过通用模板准确处理的函数,或者需要特殊操作的类型进行定义。

函数模板具体化需要注意一些要点包括:

  1. 具体化的函数模板必须在使用之前进行声明和定义。也就是说,你不能调用特化模板的代码之后才定义它。
  2. 当编译器在选择函数模板时,优先级是:最具体的匹配>模板具体化版本>通用的版本。
  3. 具体化版本并不是重载。重载发生在函数名相同但参数列表不同的情况,而具体化是针对特定类型的函数模板。
  4. 具体化的过度使用可能会导致代码变得复杂,难以阅读和维护。
  5. 不是所有类型都适合进行具体化。例如,虚函数和析构函数不能使用模板具体化。

函数模板分文件编写

由于模板的工作方式,导致模板的分文件编写存在一些困难和挑战。

模板不像常规函数和类一样的编译单元,当编译器遇到模板定义时,它并不会生成任何的代码。相反,当模板被实例化(即使特定的类型参数来使用模板)时,编译器才会生成代码。这就意味着模板的实现必须在其被实例化的地方可见,也就是说,在大多数情况下模板的定义需要放到头文件中。

然而,对于模板的具体化版本,情况有所不同。特化版本的模板在处理上更接近普通函数和类,其声明可以放在头文件中,而实现可以放在源文件中。

举个例子。假设我们有一个用于交换两个元素值的模板函数Swap。这个函数模板可以放在头文件swap.h中定义:

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

然后,假设我们希望为字符串类型提供一个特化版本的Swap,因为交换两个字符串可能需要一些额外的操作(例如,记录交换操作的日志)。我们可以在头文件中声明这个特化版本,然后在源文件中实现它:

// 在swap.h中
template <>
void Swap<std::string>(std::string& a, std::string& b);

// 在swap.cpp中
#include "swap.h"

template <>
void Swap<std::string>(std::string& a, std::string& b) {
    std::string temp = a;
    a = b;
    b = temp;
    // 记录交换操作的日志...
}

这样,我们就能在任何包含了swap.h的源文件中使用通用的Swap模板函数,也能使用为string特化的Swap函数。这就是函数模板分文件编写的基本原理和实践方法。

总结就是,模板定义通常放在头文件中,而对于具体化的模板,其声明可以放在头文件中,实现可以在源文件中。然而,这种做法可能需要根据具体的编译器或编译环境进行调整。

函数模板高级应用

decltype关键字

decltype关键字用于查询表达式的类型,这在函数模板中非常有用,他可以根据输入参数的类型推断返回类型。

#include<iostream>
using namespace std;

template<typename T1, typename T2>
auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
{
    
    return t1 + t2;
}

int main()
{
    int i = 1;
    double d = 2.2;
    cout<<add(i, d)<<endl; // 输出3.2
    return 0;
}

上述例子中,add()使用了decltype关键字,向函数传递了一个int和一个double,那么有了decltype就会返回类型double。

函数后置返回类型

int func(int x,double y);

等同:

auto func(int x,double y) -> int;

auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
{
    return t1 + t2;
}

上述例子中,函数add使用了后置返回类型,允许在函数名后面声明返回类型。这是因为在模板函数中,我们可能希望返回类型取决于模板参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tian Meng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值