类型擦除:手写 any 与 function 的原理

C++类型擦除完全指南:从原理到std::function与any实现 在C++开发中,如何让一个变量容纳任意类型的值或可调用对象,同时隐藏其真实类型?这就要依靠类型擦除(Type Erasure)机制。它结合模板的编译期灵活性与虚函数的运行时多态,将类型与操作解耦,仅保留统一接口,从而支撑起std::function、std::any等标准库组件的工作。理解类型擦除不仅能洞悉这些高级特性的底层原理,还能在事件总线、插件系统、跨模块接口等场景中设计出更通用、更稳定的方案。同时,函数指针配合void*、小对象优化、外部多态等实现技巧,也与性能调优和ABI兼容性密切相关。本文从基础 阅读详情

std::any 能装任意类型,std::function 能包任意可调用对象。这俩能力差得远,底层却是同一招:类型擦除(type erasure):用「非模板接口 + 模板派生的壳 + 虚函数跳板」把具体类型藏起来。这篇把两个都手写一遍,真跑,看完你就明白标准库这两个类在做什么、以及为什么它能省掉模板的代码膨胀。

1. 引子:一个矛盾的需求

想写一个「能装任何类型」的容器,或者「能包任何 lambda」的回调,会遇到同一个矛盾:

  • 如果用模板(template <typename T> class Box),类型在编译期确定,每个类型生成一份代码 —— 类型一多,二进制就膨胀,而且无法放进同一个 vector 里统一处理;
  • 如果用继承多态(Base* 指向 Derived),能统一放进容器,但所有类型都必须提前继承自同一个 Base —— 你没法让 int 去继承一个基类。

类型擦除是第三条路:对外只暴露一个非模板的「接口」类,内部用模板把任意类型包装成一个实现了该接口的「壳」,通过虚函数跳板访问真实数据。它既统一了接口(能放进 vector<Any>),又不要求类型有任何继承关系。

2. 手写 any:typeid 校验取值

std::any 的骨架就三步:一个抽象基类 Base(接口),一个模板派生类 Holder<T>(壳),一个 unique_ptr<Base> 成员(持有壳的指针)。取值时用 typeid 校验「你想要的类型」和「我存的类型」是不是同一个:

// any_demo.cpp — 编译: g++ -std=c++17 -Wall -O2 any_demo.cpp -o anydemo
#include <cstdio>
#include <memory>
#include <string>
#include <typeinfo>
#include <utility>

class Any {
    struct Base {                                    // 非模板接口:把所有类型统一成它
        virtual ~Base() = default;
        virtual const std::type_info& type() const = 0;
        virtual std::unique_ptr<Base> clone() const = 0;
    };
    template <typename T>
    struct Holder : Base {                           // 模板壳:真正装数据的地方
        T value;
        explicit Holder(T v) : value(std::move(v)) {}
        const std::type_info& type() const override { return typeid(T); }
        std::unique_ptr<Base> clone() const override {
            return std::make_unique<Holder<T>>(value);
        }
    };
    std::unique_ptr<Base> ptr_;

public:
    Any() = default;
    Any(const Any& other) : ptr_(other.ptr_ ? other.ptr_->clone() : nullptr) {}
    Any(Any&&) noexcept = default;
    Any& operator=(const Any& other) { ptr_ = other.ptr_ ? other.ptr_->clone() : nullptr; return *this; }
    Any& operator=(Any&&) noexcept = default;

    template <typename T>
    Any(T v) : ptr_(std::make_unique<Holder<T>>(std::move(v))) {}   // 隐式:任何类型都能塞进来

    bool has_value() const { return ptr_ != nullptr; }
    const std::type_info& type() const { return ptr_ ? ptr_->type() : typeid(void); }

    template <typename T>
    T& cast() {                                        // 取值:先 typeid 校验,再 static_cast
        if (!ptr_ || ptr_->type() != typeid(T)) throw std::bad_cast();
        return static_cast<Holder<T>*>(ptr_.get())->value;
    }
};

int main() {
    Any a = 42;
    std::printf("a 是 int = %d,值 = %d\n",
                static_cast<int>(a.type() == typeid(int)), a.cast<int>());

    Any b = std::string("hello");
    std::printf("b 是 string = %d,值 = %s\n",
                static_cast<int>(b.type() == typeid(std::string)), b.cast<std::string>().c_str());

    bool threw = false;
    try { a.cast<std::string>(); }                     // 把 int 当 string 取 → 抛异常
    catch (const std::bad_cast&) { threw = true; }
    std::printf("取错类型抛异常 = %d\n", static_cast<int>(threw));
}
a 是 int = 1,值 = 42
b 是 string = 1,值 = hello
取错类型抛异常 = 1

内存布局是这样的(概念图):

   手写 Any 的内存布局:类型擦掉,藏进虚表

   Any 对象(栈上,约 8 字节)
   ┌──────────────────┐
   │ unique_ptr<Base> ┼──►  堆上的 Holder<T>
   └──────────────────┘
                              ┌──────────────────────────────┐
                              │ vptr ──► Base 的虚函数表       │  ← 具体类型 T 被擦掉,
                              │ type()  返回 typeid(T)         │     藏进虚表指针
                              │ clone() 复制一个新的 Holder<T> │
                              │ value : T(真正的数据)         │
                              └──────────────────────────────┘

   cast<T>():先比 type()==typeid(T),对上再 static_cast 到 Holder<T> 拿 value

cast<T>() 里那句 static_cast<Holder<T>*> 之所以安全,正是因为前面已经用 typeid(T) 校验过「存的确实是 T」—— 类型擦除不是「丢掉类型信息」,而是「把类型信息藏进虚表,取用时再核对」。这也解释了为什么 any 取错类型是运行时才报(抛 bad_cast),而模板是编译期就报。

官方文档:std::any · std::type_info(typeid 返回的就是它)

3. 手写 function:invoker 跳板

std::function 的套路一模一样,只是把「存数据」换成「存一个可调用对象」,虚函数从 type() 换成 call()。这个负责调用的虚函数就是所谓的 invoker(跳板):

// function_demo.cpp — 编译: g++ -std=c++17 -Wall -O2 function_demo.cpp -o fndemo
#include <cstdio>
#include <memory>
#include <type_traits>
#include <utility>

template <typename Signature> class Function;      // 主模板:只声明,不定义

template <typename R, typename... Args>
class Function<R(Args...)> {
    struct InvokerBase {                            // 非模板接口:统一的「可调用」抽象
        virtual ~InvokerBase() = default;
        virtual R call(Args... args) = 0;
        virtual std::unique_ptr<InvokerBase> clone() const = 0;
    };
    template <typename F>
    struct Invoker : InvokerBase {                  // 模板壳:包住任意可调用对象
        F f;
        explicit Invoker(F f) : f(std::move(f)) {}
        R call(Args... args) override {             // 跳板:把虚调用转发给 f
            if constexpr (std::is_void_v<R>) {
                f(args...);
            } else {
                return f(args...);
            }
        }
        std::unique_ptr<InvokerBase> clone() const override {
            return std::make_unique<Invoker<F>>(f);
        }
    };
    std::unique_ptr<InvokerBase> inv_;

public:
    Function() = default;
    Function(const Function& other) : inv_(other.inv_ ? other.inv_->clone() : nullptr) {}
    Function(Function&&) noexcept = default;
    Function& operator=(const Function& other) { inv_ = other.inv_ ? other.inv_->clone() : nullptr; return *this; }
    Function& operator=(Function&&) noexcept = default;

    template <typename F>
    Function(F f) : inv_(std::make_unique<Invoker<F>>(std::move(f))) {}   // 任何可调用对象都能包

    R operator()(Args... args) { return inv_->call(args...); }            // 对外:像普通函数一样调
};

int add(int x, int y) { return x + y; }

int main() {
    Function<int(int, int)> f1 = add;                         // 包函数指针
    Function<int(int, int)> f2 = [](int x, int y) { return x * y; };   // 包 lambda
    Function<void(int)> printer = [](int v) { std::printf("value = %d\n", v); };   // 包 void 返回

    std::printf("add(3,4) = %d\n", f1(3, 4));
    std::printf("mul(3,4) = %d\n", f2(3, 4));
    printer(7);
}
add(3,4) = 7
mul(3,4) = 12
value = 7

调用链长这样:

   手写 Function 的调用链:虚调用只发生在跳板这一层

   Function<R(Args...)> 对象
   ┌───────────────────────────┐
   │ unique_ptr<InvokerBase>   ┼──► Invoker<F>
   └───────────────────────────┘        │ f : F(lambda / 函数指针)
                                        │ call(Args...)  ← 唯一的虚调用(跳板)
                                        └──────┬───────
   fn(1,2) ─► operator() ─► inv_->call(1,2) ─► f(1,2)
              (非模板代码)    (虚函数间接调用)   (模板实例,每种 F 一份,可内联)

这里的关键分工:Function 类和 InvokerBase 都是非模板的(编译成一份),只有 Invoker<F> 这个壳是模板(每个可调用对象类型一份,但很小,只做转发)。于是 vector<Function<int(int,int)>> 可以装下一堆签名相同、实现各异的 lambda,而不用为它们各自写一份完整的包装代码。

if constexpr (std::is_void_v<R>) 处理了「返回 void」的特例:void 函数不能 return f(...),只能「调用但不返回」。这也是 std::function 内部会用到的技巧。

官方文档:std::function · std::make_unique

4. 为什么这能省模板代码膨胀

把三种「统一不同类型」的写法放一起比:

方案类型何时确定代码份数运行时开销适合场景
模板(template<class T>)编译期每种类型一份无(可内联)类型已知、追求零开销
继承多态(Base*)运行时(基类接口)一份一次虚调用类型都继承自同一基类
类型擦除(any/function)运行时一份接口 + 每类型一个小壳一次虚调用 + 一次堆分配类型未知、但要统一接口

模板的「代码膨胀」来自:每个实例化都会把整个函数体复制一份。类型擦除的聪明之处在于,它把「必须随类型变化」的那部分压进一个极小的模板壳(Holder<T> / Invoker<F>),而「不变的大头」(接口、管理逻辑)编译成一份非模板代码。代价是每次访问多一次虚调用、对象多一次堆分配。所以类型擦除不是「免费午餐」,它是在「灵活」和「零开销」之间取了个平衡。

5. 完整示例:异构容器 + 可调用对象表

把 any 和 function 串起来:一个 vector<Any> 装异构数据按类型分派,一个 vector<Function<int(int,int)>> 装同签名不同实现的算子:

// erasure_full.cpp — 编译: g++ -std=c++17 -Wall -O2 erasure_full.cpp -o efull
#include <cstdio>
#include <memory>
#include <string>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>

class Any {
    struct Base {
        virtual ~Base() = default;
        virtual const std::type_info& type() const = 0;
        virtual std::unique_ptr<Base> clone() const = 0;
    };
    template <typename T>
    struct Holder : Base {
        T value;
        explicit Holder(T v) : value(std::move(v)) {}
        const std::type_info& type() const override { return typeid(T); }
        std::unique_ptr<Base> clone() const override { return std::make_unique<Holder<T>>(value); }
    };
    std::unique_ptr<Base> ptr_;
public:
    Any() = default;
    Any(const Any& other) : ptr_(other.ptr_ ? other.ptr_->clone() : nullptr) {}
    Any(Any&&) noexcept = default;
    template <typename T>
    Any(T v) : ptr_(std::make_unique<Holder<T>>(std::move(v))) {}
    const std::type_info& type() const { return ptr_ ? ptr_->type() : typeid(void); }
    template <typename T>
    T& cast() {
        if (!ptr_ || ptr_->type() != typeid(T)) throw std::bad_cast();
        return static_cast<Holder<T>*>(ptr_.get())->value;
    }
};

template <typename Signature> class Function;

template <typename R, typename... Args>
class Function<R(Args...)> {
    struct InvokerBase {
        virtual ~InvokerBase() = default;
        virtual R call(Args... args) = 0;
        virtual std::unique_ptr<InvokerBase> clone() const = 0;
    };
    template <typename F>
    struct Invoker : InvokerBase {
        F f;
        explicit Invoker(F f) : f(std::move(f)) {}
        R call(Args... args) override {
            if constexpr (std::is_void_v<R>) { f(args...); }
            else { return f(args...); }
        }
        std::unique_ptr<InvokerBase> clone() const override { return std::make_unique<Invoker<F>>(f); }
    };
    std::unique_ptr<InvokerBase> inv_;
public:
    Function() = default;
    Function(const Function& other) : inv_(other.inv_ ? other.inv_->clone() : nullptr) {}
    Function(Function&&) noexcept = default;
    template <typename F>
    Function(F f) : inv_(std::make_unique<Invoker<F>>(std::move(f))) {}
    R operator()(Args... args) { return inv_->call(args...); }
};

int main() {
    std::vector<Any> items;                       // 异构数据:同一个容器,装三种类型
    items.emplace_back(42);
    items.emplace_back(std::string("hello"));
    items.emplace_back(3.14);

    for (auto& item : items) {
        if (item.type() == typeid(int)) {
            std::printf("int    = %d\n", item.cast<int>());
        } else if (item.type() == typeid(std::string)) {
            std::printf("string = %s\n", item.cast<std::string>().c_str());
        } else if (item.type() == typeid(double)) {
            std::printf("double = %.2f\n", item.cast<double>());
        }
    }

    std::vector<Function<int(int, int)>> ops;     // 同签名不同实现:一个容器装三种算法
    ops.emplace_back([](int a, int b) { return a + b; });
    ops.emplace_back([](int a, int b) { return a - b; });
    ops.emplace_back([](int a, int b) { return a * b; });

    for (auto& op : ops) {
        std::printf("op(6,3) = %d\n", op(6, 3));
    }
}
int    = 42
string = hello
double = 3.14
op(6,3) = 9
op(6,3) = 3
op(6,3) = 18

同一个 vector<Any> 装下了 int、std::string、double 三种毫无继承关系的类型,靠 typeid 分派;同一个 vector<Function<int(int,int)>> 装下了三个不同的 lambda,靠 invoker 跳板统一调用。这正是类型擦除的价值:把「类型未知」的灵活性,和「统一接口」的工程性,同时拿到了手。代价只是每次访问多一次虚调用、一次堆分配。

6. 延伸阅读

本知识库内的相关篇目:

7. 一句话总结

类型擦除 = 非模板接口(Base / InvokerBase)+ 模板派生壳(Holder<T> / Invoker<F>)+ 虚函数跳板(type() / call()),把具体类型藏进虚表、取用时再核对 —— any 用 typeid 校验后 static_cast 取值、取错抛 bad_cast;function 用 invoker 虚调用把 operator() 转发给任意 lambda;这套机制把「必须随类型变化」的部分压进极小模板壳、不变的大头编译成一份,从而在「类型未知」和「代码不膨胀」之间取到平衡,代价是每次访问一次虚调用加一次堆分配。

C++11包装器原理与实战:从std::function到类型擦除 C++中的函数包装器是一类实现可调用对象统一调度的基础设施,其核心是类型擦除机制——在静态类型系统中安全容纳函数指针、成员函数、lambda和仿函数等异构 callable。它依托变参模板、右值引用与完美转发等C++11关键特性构建,通过虚函数分发或小缓冲优化实现运行时多态接口。技术价值在于解耦回调系统、事件总线与插件架构,支撑松耦合、可测试的现代C++工程实践。典型应用场景包括GUI事件处理、异步任务队列及跨模块通信。本文深入剖析std::function设计本质,并结合lambda表达式与类型擦除原理展 阅读详情

相关推荐

C++类型擦除实战:std::function与std::any的实现原理和工程取舍

在C++工程中,当需要将不同类型的对象放入同一容器并保持安全访问时,类型擦除是一项关键技术。它通过将具体类型转换为虚函数表,在保留值语义的同时实现运行时多态,解决了继承体系僵化和void*丢失类型信息的问题。标准库的std::function和std::any正是这一思想的典型实现,分别用于统一可调用对象和存储任意值。理解其底层原理,有助于在事件分发、回调注册表、对象池等场景中做出正确设计。本文从手写类型擦除的核心步骤讲起,剖析外部多态模式,并深入讨论性能开销、小对象优化、代码膨胀及常见陷阱,帮助开发者在灵

weixin_31155097的博客 256

C++类型擦除:从原理到实战,手写std::function与std::any

在C++工程中,模板提供编译期的静态多态,而虚函数则实现运行期的动态分派,两者各有优劣。类型擦除巧妙融合二者,在编译期隐藏具体类型,在运行期通过统一接口操作对象,解决了回调容器、异构存储、模块解耦等场景中“类型无法统一”的核心矛盾。从最基础的std::any入手,理解外部类+Concept基类+Model模板子类的经典三件套结构,能够清晰揭示拷贝、移动、类型检查等底层机制。进一步走向std::function,可掌握可调用对象的统一封装,以及小对象优化、性能代价等工程细节。理解类型擦除,不仅有助于更深刻地使

weixin_32183107的博客 224

深入理解C++类型擦除:从std::function到std::any的底层原理与工程实践

多态是面向对象程序设计的核心思想,C++中主要依靠模板与虚函数来呈现。模板在编译期确定类型,性能高但缺乏运行期灵活性;虚函数则把决策推迟到运行时,却要求类型必须继承同一基类。类型擦除巧妙地融合了两者优势,通过“抽象基类+模板派生类”的统一外壳,隐藏具体对象类型,只暴露约定接口。其底层依赖虚表与函数指针,标准库中的std::function和std::any正是这一技术的典型代表。理解类型擦除,有助于开发者解决异构容器、回调封装、事件分发、插件接口等场景中的设计难题,也能深入洞察现代C++标准库的实现机制。本

weixin_34116482的博客 222

C++类型擦除详解:从原理到实现,统一异质对象调用的核心技巧

C++模板和继承各有局限:模板在编译期确定类型,难以实现运行时统一存储;继承则要求类型必须继承自共同基类,对第三方库不友好。类型擦除(Type Erasure)通过“非模板外壳+虚函数分发”隐藏真实类型,只保留行为接口,成为连接编译期强类型与运行期多态的桥梁。从std::function到std::any,标准库中的大量设施正是这一思想的工程实践。在事件系统、插件机制或游戏场景管理中,类型擦除让不同对象能统一进入容器并响应同一操作,同时带来显著的设计灵活性。本文从概念原理切入,对比多态方案选型,并给出手写类

weixin_31960565的博客 177

C++类型擦除技术:原理、实现与应用实践

类型擦除是C++中实现运行时多态的重要技术,它通过抽象接口与模板的结合,在保持类型安全的同时处理多种数据类型。其核心原理是利用虚函数表实现动态分发,配合模板技术保存具体类型信息。这种技术在STL组件如std::function和std::any中得到典型应用,既能实现类似动态语言的灵活性,又保留了C++的静态类型检查优势。在工程实践中,类型擦除特别适用于插件系统、回调机制和异构容器等场景,配合小型对象优化(SOO)技术可显著提升性能。现代C++标准不断强化类型擦除支持,如C++20的concept和C++2

weixin_32466193的博客 235

C++17 std::any:类型安全容器原理与实战应用

类型擦除是一种编程技术,它允许在运行时处理多种数据类型,同时隐藏具体类型信息。其核心原理是通过虚函数表或函数指针,将类型特定的操作抽象为通用接口,从而实现编译时类型与运行时操作的解耦。这项技术的核心价值在于提供了处理异构数据的灵活性,尤其适用于需要动态处理未知类型的场景,如插件系统、消息传递和配置管理。在C++中,std::any 是标准库提供的类型安全容器,它基于类型擦除实现,能够存储任意类型的值,并通过 std::any_cast 进行安全的类型转换。相比于不安全的 void* 指针,std::any

weixin_34377919的博客 458

C++类型擦除:从std::function到std::any的底层原理与工程实践

C++开发中,模板与虚函数各自存在局限:模板要求编译期确定类型,虚函数则强依赖公共基类。类型擦除正是为解决这一矛盾而生,其核心机制是用模板在编译期捕获具体类型,再借虚函数在运行期统一行为,从而将“具体类型”转化为“行为契约”。这一技术价值在回调系统设计中尤为突出:std::function 能收纳任意签名的可调用对象,std::any 能安全存储任意类型数据,为事件总线、插件体系等场景提供统一接口。理解底层原理后,还需掌握其工程边界,包括性能成本、小对象优化以及何时选择 variant 或虚函数。从手写简化

weixin_33423874的博客 314

MobaXterm终端工具个人学习版安装包及常用配置备份

资源包含 MobaXterm 个人版的安装包以及日常运维常用的基础配置备份文件。适用于网络工程师及运维人员用于日常的服务器管理、SSH 连接及串口调试。 压缩包内附带了个人整理的软件使用技巧与配置说明(仅供个人学习参考)。请各位同行下载后注意,技术工具请尽量支持官方正版,本资源仅供个人测试与交流使用,切勿用于商业环境。

C++类型擦除全解析:std::function、any与variant原理及实战

在C++开发中,模板与虚函数分别代表了静态与运行时的多态范式,而类型擦除(type erasure)则巧妙地在两者之间取得平衡。它通过统一的外部接口隐藏对象的真实类型,同时借助模板捕获具体类型、虚函数完成动态分发,从而在不牺牲类型安全的前提下,让不同类型对象能够放入同一容器、按统一方式调用。std::function、std::any、std::variant 是标准库中最具代表性的三种实现,分别适用于回调策略注入、任意类型存储与编译期类型联合等场景。理解其内部机制与性能权衡,有助于设计通用组件、事件总线或

weixin_31856057的博客 127

VMware VSAN 高级参数介绍(测试用)

VSAN 的策略驱动让我们看到了VSAN无穷的潜力,其设计思路相当新颖,在一个基础资源池上使用策略就能实现不同对象的区分服务,而目前没有其他厂家能到VMware这样的高度。 VSAN 呈现给用户配置很简单,但实际上设计及后台比较复杂。笔者无意间发现了VSAN的隐藏高级参数设置,有很多好玩的选项想分享出来。这些参数全部是针对每主机进行设置的。VSAN 中有很多默认设置,这些设置应该都是研发人员多次测试得出的最优值,修改可能导致环境异常,并不建议修改! 目录 参数1:全闪配置下 SSD 写缓存最大.

山水牧羊 2031

Oracle 11g RAC 节点通信故障排查与修复指南

本文详细介绍了Oracle 11g RAC集群发生节点通信故障时的系统性排查与修复方法。当出现节点异常重启时,核心问题往往源于心跳网络中断。文章提供了从快速诊断、日志分析到六层深度检查的完整指南,并给出了针对网络配置、内核参数及Oracle已知Bug等常见根因的实战解决方案,帮助DBA快速恢复集群稳定。

weixin_29246195的博客 273

从 std::function 出发,一文吃透 C++ 类型擦除(Type Erasure):原理、手写实现与开源方案

一句话定义:类型擦除 = "把具体类型藏起来,只暴露一个统一的、运行时才知道的接口"。快递盒子类比(强烈建议先理解这个)你寄快递时,不会把手机、书、螺丝刀直接裸露着塞进货车,而是先装进大小不一的纸箱,再把纸箱搬上车。纸箱。纸箱 = 统一的外壳(std::function / std::any 的"外壳"类型);手机 / 书 / 螺丝刀 = 具体类型(int、MyClass、某个 lambda);搬箱工人 = 调用方(不需要知道箱子里是什么,只管搬);关键。

ITOfDragon的博客 313

基于FPGA的ov5640摄像头简易图像采集装置详解

本文介绍一种基于FPGA的,通过流模式DDR实现OV5640摄像头数据采集并显示的方法。

qq_42230252的博客 1万+

C++类型擦除原理与实现:从std::function到运行时多态

类型擦除是C++中连接编译期模板与运行时多态的关键技术,其核心思想是在隐藏具体类型的同时保留操作能力,让异质对象可以统一存储和调用。std::function和std::any正是基于这一机制的标准库实现,前者擦除可调用对象的调用行为,后者擦除任意值的类型信息。理解vtable与函数指针的底层支撑后,开发者可以更清楚地判断何时使用标准库工具,何时手写Concept/Model架构。类型擦除适用于回调注册、配置存储、插件参数透传等类型集合开放的场景,但也需权衡虚函数调用、堆分配与缓存效率。掌握其拷贝语义、小对

weixin_31528001的博客 120

下水管道损坏检测数据集总结

下水管道损坏检测数据集包含 6708 张图像,为下水管道损坏情况的检测提供了丰富的数据基础。该数据集提供了 YOLO 和 VOC 两种常见的标注方式,方便不同需求的使用者进行模型训练和研究。

crz1336768325的博客 870

基于YOLOv5的空间目标智能监测系统:航天器与太空碎片识别技术实现

做完这个项目,你会发现自己完整走过了“数据采集→标注→模型训练→功能集成→界面开发”的毕设全流程,这不仅是技术能力的证明,更是对“技术守护宇宙安全”这一理念的实践。答辩时,你可以重点讲这几点:系统解决了航天监测的哪些痛点、YOLOv5在太空目标检测中的性能优势、系统未来能怎么优化(比如对接航天监测网络、实现实时轨道预测)。未来,这个系统还能往“更智能”的方向发展:比如结合轨道计算预测碰撞风险、开发移动端APP让天文爱好者参与太空垃圾上报、在航天任务中实现全自动避障决策……想象空间很大。

m0_64661770的博客 111

c++系列-类型擦除

一个常见的例子是 `std::any` 和 `std::function`,它们利用类型擦除来存储和调用不同类型的对象。1. **运行时开销**: 类型擦除通常通过虚函数、内存分配和动态类型检查实现,这会增加一定的运行时开销(例如 `std::function` 和 `std::any` 中的额外内存分配和类型存储)。3. **存储类型信息**: 在某些类型擦除的实现中(如 `std::any`),底层存储对象的类型信息,以便在需要时(例如通过 `std::any_cast`)重新获得该类型。

weixin_57257683的博客 605

FPGA控制AD5754实现模数转换Verilog代码与测试激励

本文提供了使用FPGA控制AD5754芯片实现数字信号转模拟信号的Verilog代码和测试激励。AD5754芯片简介:AD5754是一款16位数字信号转模拟信号的芯片。通信方式:FPGA与AD5754之间通过SPI接口进行通信,SPI时钟clk的最大频率为30MHz。模拟输出:AD5754提供了四路模拟输出。工作方式:当前AD5754采用单极性工作方式。指令刷新:根据现有理解,同轴的速度和电流指令应同时刷新。利用LDAC引脚控制模拟信号的刷新,但需实际验证。CLR信号验证。

whm128的博客 246

C++类型擦除技术:原理、实现与应用场景

类型擦除(Type Erasure)是C++中实现运行时多态的重要技术,通过抽象接口与模板派生类的组合,允许代码处理未知具体类型的对象。其核心原理基于虚函数动态分派,在std::function、std::any等标准库组件中广泛应用。该技术特别适合需要处理异构类型的场景,如事件系统、插件架构等,能有效降低代码耦合度。现代C++通过小对象优化、移动语义等技术提升性能,而C++20概念(Concepts)进一步增强了类型安全性。开发者需权衡其带来的灵活性与虚函数调用、内存分配等开销,在跨平台开发、回调系统等场

weixin_33806859的博客 20

MNI152 坐标空间和 fsaverage 坐标空间相互转换, 如已知fsaverage空间的坐标如何求MNI152 坐标?aparc.a2009s.annot转化到MNI152空间?

大多数神经影像学研究的结果是在体积(例如,MNI152)或表面(例如,fsaverage)坐标系中报告的。体积坐标系和表面坐标系之间的准确映射可以促进许多应用,例如将 fMRI 组分析从 MNI152 投影到 fsaverage 以进行可视化或从 fsaverage 投影静息状态 fMRI 分区对 MNI152进行新数据的体积分析。Wu et al., 2018 评估了三种在 MNI152通过模拟上述应用的 Colin27 和 fsaverage 坐标系:将组平均数据从 MNI152/Colin27 投影到

从事脑科学核磁共振方法学研究,在Nature communications等权威期刊发表研究论文,熟练掌握磁共振处理方法和统计学方法,欢迎大家和我交流。 2966

HighTec安装使用

HighTec安装使用

qq_43552564的博客 3835

C++类型擦除技术:原理、实现与应用

类型擦除(Type Erasure)是C++中实现运行时多态的重要技术,它通过在编译期保留类型信息、运行期隐藏具体类型,解决了泛型编程中的类型耦合问题。其核心原理基于虚函数分派或函数指针,通过抽象接口统一操作不同具体类型。该技术在标准库组件如std::function和std::any中得到典型应用,既能保持接口简洁性,又能避免模板参数爆炸。在事件系统、插件架构等场景中,类型擦除技术展现出独特价值,特别是结合小型对象优化(Small Object Optimization)后,可显著提升性能。现代C++还引

weixin_30225755的博客 55

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

上一篇: noexcept 为什么移动构造必须标:从 vector 扩容退化成拷贝说起
下一篇: std::variant 完全指南:类型安全的 union 与 std::visit 用法
无名猿
博客等级 码龄10年 61粉丝 184原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值