C++的类型擦除记录

1.概述

C++ 类型擦除是指通过一些技术手段去掉(或“擦除”)C++ 中的类型信息,使得一个数据结构或算法能够处理不同类型的对象。类型擦除可以使得泛型编程变得更加灵活和通用,可以将相同的算法应用于不同类型的数据上,而无需对算法进行修改。在C++中,有两种主要的类型擦除方法,分别是基于模板类的类型擦除和基于虚函数的类型擦除。

  1. 基于模板类的类型擦除:这种类型擦除方法主要是通过定义通用的模板类来实现。标准模板库中的容器类(如vector、list等)就是基于模板类的类型擦除实现的。
  2. 基于多态的类型擦除:这种类型擦除方法主要是通过定义一个带有虚函数的“擦除器”类来实现。

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基类,以及两个派生类IntDataStringData,分别用于存储整型和字符串类型的数据。接下来就可以像无类型一样使用这些类型了。

int main() {
    std::vector<Data*> 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类模板,用于存储任意类型的值,类似于一个动态的unionstd::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<int>(a);  // 获取a中存储的int类型值

3.2.std::variant

C++17标准引入了另一个有用的类模板std::variantstd::variant可以存储多种不同类型的变量,但它与std::any有所不同的是,它所存储的不同的数据类型必须在编译时就确定,不支持动态类型。

std::variant<int, double, std::string> v = 10;
int value = std::get<int>(v);  // 获取存储在v中的int类型值

3.3.std::tuple

C++11标准中引入了std::tuple类模板,用于存储任意数量和任意类型的值。

std::tuple<int, double, std::string> t(10, 3.14, "Hello, World!");
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 <iostream>
#include <functional>

int add(int a, int b) {
    return a + b;
}

int main() {
    std::function<int(int, int)> f;
    f = add;
    f = [](int a, int b) { return a * b; };

    return 0;
}

4.总结

基于模板的类型擦除和基于多态的类型擦除都是实现类型擦除的方式,但它们有以下几个方面的不同:

原理不同:基于模板的类型擦除是在编译期完成的,通过对通用代码的特化来实现类型擦除;而基于多态的类型擦除是在运行期完成的,通过虚函数机制来实现类型擦除。

实现复杂度不同:基于模板的类型擦除通常需要使用模板特化、SFINAE 和模板元编程等高级技术,并且在类型转换时需要手动处理类型匹配和转换的细节,实现比较复杂;而基于多态的类型擦除在语义上更为简单,只需要让基类对象指向派生类对象即可。

转载至:https://zhuanlan.zhihu.com/p/620265683

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值