网站首页 > 技术文章 正文
在 C++ 编程中,我们经常会遇到需要编写独立于其操作类型(包括不同函数类型和变量类型)的情况(类似于C语言中的回调函数,但是能适配任意类型)。这时,类型擦除 (Type Erasure) 就派上用场了,它让我们能够以统一的方式处理各种类型。本文将探讨类型擦除的定义、工作原理,并提供实现 std::function 和 std::any 的示例。
什么是类型擦除
类型擦除是一种无需继承或模板即可实现多态性的语言规范。它隐藏了对象的具体类型,只暴露一个统一的接口。这使我们能够通过一个通用接口存储、传递和操作不同类型的对象,而无需在编译时知道具体类型。
为什么使用类型擦除
类型擦除有几个主要优点:
灵活性:你可以通过一个接口处理不同类型的对象。
解耦:使用类型擦除对象的代码在编译时无需知道具体类型。
多态性:无需继承即可实现运行时的多态性,从而更容易地处理不共享公共基类的现有类型。
实现类型擦除:std::function 和 std::any
C++ 标准库提供了 std::function 和 std::any,它们是类型擦除的示例。一起看看它们的工作原理。
简化的 std::function
std::function 是一个多功能的可调用对象包装器。它可以存储函数、lambda 表达式或定义了()运算操作符的对象。std::function 的关键特性在于它隐藏了可调用对象的类型,从而统一了调用接口。
示例:
#include <iostream>
#include <memory>
#include <utility>
class CallableBase { //没有类型的接口类
public:
virtual ~CallableBase() = default;
virtual void call() const = 0; //纯虚函数
};
template <typename T> //用这个子类对接口类重载,赋值具体类型的函数
class CallableWrapper : public CallableBase{
public:
CallableWrapper(T callable) :callable_(std::move(callable)) {}
void call() const override {
callable_();
}
private:
T callable_;
};
class SimpleFunction{
public:
template <typename T>
SimpleFunction(T callable): //创建时赋值初始化
callableImpl_(std::make_shared<CallableWrapper<T>>(std::move(callable))) {}
void operand() const {//()操作符
if(callableImpl_)
callableImpl_->call();
}
private:
std::shared_ptr<CallableBase> callableImpl_; //无类型的接口变量
};
void helloFunction(){
std::cout <<"Hello, function !!! \n";
}
int main(){
SimpleFunction f1 = helloFunction;
SimpleFunction f2 = [](){std::cout << "Hello, Lambda !!!\n";};
f1(); //输出:Hello, Function !!!
f2(); //输出:Hello, Lambda !!!
return 0;
}
在此例中,SimpleFunction 用于存储常规函数和lambda 表达式。被调用函数的实际类型被擦除了,并且 SimpleFunction 提供了一种统一的方法来调用存储的函数。
简化的 std::any
std::any 是一个类型安全的容器,可容纳任何类型的单个值。它可以存储任何类型的对象,并且可以使用类型安全的强制类型转换来检索存储的值。
#include <iostream>
#include <memory>
#include <utility>
#include <typeinfo>
#include <typeindex>
class AnyBase{
public:
virtual ~AnyBase() = default;
virtual std::type_index type() const = 0;
virtual std::unique_ptr<AnyBase> clone() = 0;
};
template <typename T>
class AnyWrapper : public AnyBase{
public:
explicit AnyWrapper(T value) : value_(std::move(value)){}
std::type_index type() const override { //存储了类型信息,类型转换时用
return typeid(T);
}
std::unique_ptr<AnyBase> clone() override{
return std::make_unique<AnyWrapper<T>>(value_);
}
T& get(){
return value_;
}
private:
T value_;
};
class SimpleAny{
public:
SimpleAny() = default;
template <typename T>
SimpleAny(T value)
: value_(std::make_unique<AnyWrapper<T>>(std::move(value))){}
SimpleAny(const SimpleAny& other)
: value_(other.value_ ? other.value_->clone() : nullptr){}
SimpleAny& operator=(const SimpleAny& other) {
if (this != &other) {
value_ = other.value_ ? other.value_->clone() : nullptr;
}
return *this;
}
std::type_index type() const{
return value_ ? value_->type() : typeid(void);
}
template <typename T>
T& get(){
if (type() != typeid(T)) { //类型转换时判断是否类型匹配
throw std::bad_cast();
}
return static_cast<AnyWrapper<T>*>(value_.get())->get();
}
private:
std::unique_ptr<AnyBase> value_;
};
int main(){
SimpleAny a(10);
std::cout << "Stored int: " << a.get<int>() << "\n";
a = std::string("Hello, Any!");
std::cout << "Stored string: " << a.get<std::string>() << "\n";
return 0;
}
本例中,“SimpleAny”用于存储一个 int 类型,然后存储一个 std::string 类型。
猜你喜欢
- 2025-08-05 KKR Nears Completion of Dayao Soda Buyout in Rare Foreign Takeover of Chinese Beverage Brand
- 2025-08-05 The Rise of China’s Machine Tool Industry Despite the West's Export Restrictions
- 2025-08-05 Tesla Logs Largest Revenue Decline in Over A Decade as Q2 EV Sales Continues to Plunge
- 2025-08-05 Chinese vice premier calls for championing humanity's common values, promoting multipolar world
- 2025-08-05 China Unveils 600 km/h Superconducting Maglev Train, Expected to Slash Beijing–Shanghai Travel Time to 2.5 Hours
- 2025-08-05 Partnership can once again prove its mettle
- 2025-08-05 Amundi sees "US Exceptionalism" eroding, while turns bullish on China's AI
- 2025-08-05 China's listed banks attract record investor visits on dividend appeal
- 2025-08-05 Huawei Denies Plagiarism in Pangu AI Model After Allegations of Copying Alibaba's Qwen
- 2025-08-05 US consumers 'eat' force-fed tariffs
- 10-02基于深度学习的铸件缺陷检测_如何控制和检测铸件缺陷?有缺陷铸件如何处置?
- 10-02Linux Mint 22.1 Cinnamon Edition 搭建深度学习环境
- 10-02AWD-LSTM语言模型是如何实现的_lstm语言模型
- 10-02NVIDIA Jetson Nano 2GB 系列文章(53):TAO模型训练工具简介
- 10-02使用ONNX和Torchscript加快推理速度的测试
- 10-02tensorflow GPU环境安装踩坑日记_tensorflow配置gpu环境
- 10-02Keye-VL-1.5-8B 快手 Keye-VL— 腾讯云两卡 32GB GPU保姆级部署指南
- 10-02Gateway_gateways
- 最近发表
-
- 基于深度学习的铸件缺陷检测_如何控制和检测铸件缺陷?有缺陷铸件如何处置?
- Linux Mint 22.1 Cinnamon Edition 搭建深度学习环境
- AWD-LSTM语言模型是如何实现的_lstm语言模型
- NVIDIA Jetson Nano 2GB 系列文章(53):TAO模型训练工具简介
- 使用ONNX和Torchscript加快推理速度的测试
- tensorflow GPU环境安装踩坑日记_tensorflow配置gpu环境
- Keye-VL-1.5-8B 快手 Keye-VL— 腾讯云两卡 32GB GPU保姆级部署指南
- Gateway_gateways
- Coze开源本地部署教程_开源canopen
- 扣子开源本地部署教程 丨Coze智能体小白喂饭级指南
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)