You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多可能性下的C++模板推导问题及Wrapper类设计咨询

多态Wrapper相关问题解答

原始代码

struct Wrapper
{
   vector<shared_ptr<BaseType>> data;

   template <typename DerivedType>
   add(const DerivedType& e)
   {
     data.push_back(make_shared<DerivedType>(e));
   };
}

该Wrapper的设计目标是实现多态,希望能将存入的任意对象转换回其原始DerivedType。现有派生自A的类B,执行wrapper.add(B());时,模板推导会将DerivedType解析为B。


问题1:由于B()的基类是A,模板推导是否也能将DerivedType解析为A(即使A并非其最派生类型)?

默认情况下不行。模板参数推导完全基于传入实参的实际类型,你传的是B(),实参类型就是B,所以编译器只会推导DerivedType为B。如果要让推导结果是A,要么显式把实参转成A类型(比如wrapper.add(static_cast<A>(B()));),要么直接显式指定模板参数(wrapper.add<A>(B());),否则编译器不会自动“降级”到基类类型。

问题2:如果可以,make_shared<A>(e)生成的shared_ptr能否被动态转换为shared_ptr?

绝对不行。make_shared<A>(e)会创建一个纯A类型的对象——传入的B对象会被“切片”,只保留基类A的部分,内存里根本没有B的成员数据和虚表信息。dynamic_pointer_cast<B>要求被转换的指针实际指向B类型的对象,否则会返回空指针,这里显然不满足条件,转换必然失败。

问题3:如果不行,是否有更优的Wrapper设计方案?要求Wrapper可复制,我希望避免显式指定模板参数。

可以通过类型擦除的方式实现,核心是让Wrapper存储的对象“记住”自己的原始类型,同时支持复制操作。这里提供两种可行方案:

方案1:无基类依赖的类型擦除Wrapper

适合不需要统一基类的场景,直接存储任意类型的对象,并支持转换回原始类型:

#include <vector>
#include <memory>

struct AnyHolder {
    virtual ~AnyHolder() = default;
    virtual std::unique_ptr<AnyHolder> clone() const = 0; // 支持Wrapper复制
    virtual void* get() = 0;
};

template <typename T>
struct Holder : AnyHolder {
    T value;
    Holder(const T& v) : value(v) {}
    std::unique_ptr<AnyHolder> clone() const override {
        return std::make_unique<Holder<T>>(value);
    }
    void* get() override {
        return &value;
    }
};

struct Wrapper {
    std::vector<std::unique_ptr<AnyHolder>> data;

    template <typename DerivedType>
    void add(const DerivedType& e) {
        data.push_back(std::make_unique<Holder<DerivedType>>(e));
    }

    // 转换回原始类型,返回nullptr表示索引越界或类型不匹配
    template <typename T>
    T* get(size_t index) {
        if (index >= data.size()) return nullptr;
        return static_cast<T*>(data[index]->get());
    }

    // 复制构造函数
    Wrapper(const Wrapper& other) {
        for (const auto& holder : other.data) {
            data.push_back(holder->clone());
        }
    }

    // 复制赋值运算符
    Wrapper& operator=(const Wrapper& other) {
        if (this != &other) {
            data.clear();
            for (const auto& holder : other.data) {
                data.push_back(holder->clone());
            }
        }
        return *this;
    }

    // 移动构造和赋值默认生成
    Wrapper(Wrapper&&) = default;
    Wrapper& operator=(Wrapper&&) = default;
};

使用示例:

Wrapper w;
w.add(B());
B* b_ptr = w.get<B>(0); // 直接拿到B类型指针

方案2:保留多态基类的类型擦除Wrapper

如果需要保留A作为统一基类,同时支持转换回原始派生类型,可以结合type_index做类型校验:

#include <vector>
#include <memory>
#include <typeindex>

struct A { virtual ~A() = default; };
struct B : A {};

struct AnyHolder {
    virtual ~AnyHolder() = default;
    virtual std::unique_ptr<AnyHolder> clone() const = 0;
    virtual std::shared_ptr<A> get_base() const = 0;
    virtual std::type_index type() const = 0;
    virtual void* get_raw() const = 0;
};

template <typename T>
struct Holder : AnyHolder {
    std::shared_ptr<T> value;
    Holder(const T& v) : value(std::make_shared<T>(v)) {}
    std::unique_ptr<AnyHolder> clone() const override {
        return std::make_unique<Holder<T>>(*value);
    }
    std::shared_ptr<A> get_base() const override {
        return value;
    }
    std::type_index type() const override {
        return std::type_index(typeid(T));
    }
    void* get_raw() const override {
        return value.get();
    }
};

struct Wrapper {
    std::vector<std::unique_ptr<AnyHolder>> data;

    template <typename DerivedType>
    void add(const DerivedType& e) {
        static_assert(std::is_base_of_v<A, DerivedType>, "类型必须派生自A");
        data.push_back(std::make_unique<Holder<DerivedType>>(e));
    }

    // 安全转换回原始类型,类型不匹配或索引越界返回空指针
    template <typename T>
    std::shared_ptr<T> get(size_t index) {
        if (index >= data.size() || data[index]->type() != std::type_index(typeid(T))) {
            return nullptr;
        }
        return std::static_pointer_cast<T>(std::shared_ptr<T>(data[index]->get_raw()));
    }

    // 复制构造和赋值同方案1
    Wrapper(const Wrapper& other) {
        for (const auto& holder : other.data) {
            data.push_back(holder->clone());
        }
    }

    Wrapper& operator=(const Wrapper& other) {
        if (this != &other) {
            data.clear();
            for (const auto& holder : other.data) {
                data.push_back(holder->clone());
            }
        }
        return *this;
    }

    Wrapper(Wrapper&&) = default;
    Wrapper& operator=(Wrapper&&) = default;
};

这两个方案都满足:无需显式指定模板参数、Wrapper可复制、能安全转换回原始派生类型。


内容的提问来源于stack exchange,提问作者François

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 12:54:50