新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 解说C++ 的类型擦除:std::function 和std::any

解说C++ 的类型擦除:std::function 和std::any

作者:嵌入式经验分享 时间:2025-08-04 来源:今日头条 收藏

编程中,我们经常会遇到需要编写独立于其操作类型(包括不同函数类型和变量类型)的情况(类似于C语言中的回调函数,但是能适配任意类型)。这时, (Type Erasure) 就派上用场了,它让我们能够以统一的方式处理各种类型。本文将探讨的定义、工作原理,并提供实现 std::function 和 std::any 的示例。

什么是

类型擦除是一种无需继承或模板即可实现多态性的语言规范。它隐藏了对象的具体类型,只暴露一个统一的接口。这使我们能够通过一个通用接口存储、传递和操作不同类型的对象,而无需在编译时知道具体类型。

为什么使用类型擦除

类型擦除有几个主要优点:

灵活性:你可以通过一个接口处理不同类型的对象。

解耦:使用类型擦除对象的代码在编译时无需知道具体类型。

多态性:无需继承即可实现运行时的多态性,从而更容易地处理不共享公共基类的现有类型。

实现类型擦除:std::function 和 std::any

标准库提供了 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 类型。


关键词: C++ 类型擦除

评论


技术专区

关闭