1、概述
C++ 类型擦除是指通过一些技术手段去掉(或“擦除”)C++ 中的类型信息,使得一个数据结构或算法能够处理不同类型的对象。类型擦除可以使得泛型编程变得更加灵活和通用,可以将相同的算法应用于不同类型的数据上,而无需对算法进行修改。在C++中,有两种主要的类型擦除方法,分别是基于模板类的类型擦除和基于虚函数的类型擦除。
基于模板类的类型擦除:这种类型擦除方法主要是通过定义通用的模板类来实现。标准模板库中的容器类(如vector、list等)就是基于模板类的类型擦除实现的。基于多态的类型擦除:这种类型擦除方法主要是通过定义一个带有虚函数的“擦除器”类来实现。
2、基于多态的类型擦除
基于多态的类型擦除是指使用多态性和虚函数机制来实现类型擦除的方法。该方法主要是通过定义一个基类来实现,该基类包含一组虚函数,用于操作实际数据类型的存储和访问,并定义一个或多个派生类用于具体存储和访问不同类型的数据。 以下是一个基于多态的类型擦除的例子,演示了如何使用基类指针来访问不同类型的数据。
class Data {
};
class IntData : public Data {
public:
IntData(int value) : _value(value) {}
private:
int _value;
};
class StringData : public Data {
public:
StringData(std::string value) : _value(value) {}
private:
std::string _value;
};
该例子定义了一个Data基类,以及两个派生类IntData和StringData,分别用于存储整型和字符串类型的数据。接下来就可以像无类型一样使用这些类型了。
int main() {
std::vector dataList {
new IntData(123),
new StringData("Hello, world!")
};
for(auto data : dataList) {
delete data;
}
return 0;
}
3、基于模板的类型擦除
基于模板的类型擦除是指使用模板类的参数推导机制,在编译时自动生成不同类型的具体实现,从而实现类型无关的操作的方式。C++标准库内的模板都属于类型擦除的应用,比较典型的用于类型擦除的容器有以下几种:
std::any:C++17 新增的容器,可以存储任意类型的对象。可以通过 std::any_cast 转化为指定类型的对象;std::variant:C++17 新增的容器,可以存储多个可能的类型之一的对象,可以通过 std::get 和 std::visit 访问其中的对象;std::function:C++11 新增的容器,可以存储任何可调用对象的函数对象,包括函数指针、成员函数指针、仿函数等等;std::tuple:C++11 中提供的容器,可以存储多个可能不同类型的值。
接下来介绍一下这4类类型擦除容器的最基本用法。
3.1、std::any
C++17标准引入了std::any类模板,用于存储任意类型的值,类似于一个动态的union。std::any的主要功能是可以在运行时存储各种类型的数据类型,并且可以动态地查看和转换这些数据类型。
std::any a; // 创建一个空的std::any对象
std::string str = "Hello, World!";
std::any a = str; // 创建一个存储std::string类型值的std::any对象
std::any a = 10; // 创建一个存储int类型值的std::any对象
int value = std::any_cast
3.2、std::variant
C++17标准引入了另一个有用的类模板std::variant,std::variant可以存储多种不同类型的变量,但它与std::any有所不同的是,它所存储的不同的数据类型必须在编译时就确定,不支持动态类型。
std::variant
int value = std::get
3.3、std::tuple
C++11标准中引入了std::tuple类模板,用于存储任意数量和任意类型的值。
std::tuple
int i = std::get<0>(t); // 获取第一个值,即10
double d = std::get<1>(t); // 获取第二个值,即3.14
std::string str = std::get<2>(t); // 获取第三个值,即"Hello, World!"
std::get<0>(t) = 20; // 将第一个值从10修改为20
3.4、std::function
std::function是C++11标准库中提供的一个函数对象包装器,它可以存储任何可调用对象(函数、函数指针、成员函数指针、Lambda表达式等)。使用std::function的好处是,它能够存储任何可调用对象,而不需要具体指定其类型,从而实现了类型的擦除,使得我们能够更加灵活地处理不同类型的函数,并在函数调用时自动进行参数类型检查。以下是std::function的类型擦除例子:
#include
#include
int add(int a, int b) {
return a + b;
}
int main() {
std::function
f = add;
f = [](int a, int b) { return a * b; };
return 0;
}
相关链接
发表评论