如何利用捕获的构造函数参数创建新的Base类实例(C++17)
解决方案
要解决从std::vector<std::any>构造Base实例的问题,核心是保留原始构造参数的类型信息,在需要时将std::any中的值转换回正确类型后传递给Base的构造函数。以下是C++17下的实现方案:
1. 新增辅助构造逻辑
添加一个辅助命名空间,实现基于索引序列的参数转换与构造:
namespace detail { template <typename Base, typename... Args, std::size_t... Is> Base construct_base_from_args(const std::vector<std::any>& args, std::index_sequence<Is...>) { // 编译期校验参数数量是否匹配 static_assert(sizeof...(Args) == args.size(), "参数数量不匹配"); // 将每个std::any转换为原始类型,调用Base构造函数 return Base(std::any_cast<std::decay_t<Args>>(args[Is])...); } }
2. 修改Child类实现
在Child类中新增可调用对象存储构造逻辑,同时保留参数修改能力:
template<class Base> class Child : public Base { public: template <typename... Args> Child(Args&&... args) : Base(std::forward<Args>(args)...), m_foo(get_penultimate(std::forward<Args>(args)...)) { // 存储参数副本到m_args (m_args.emplace_back(std::forward<Args>(args)), ...); // 保存构造逻辑:绑定原始参数类型,生成构造器 m_constructor = [this]() -> Base { return detail::construct_base_from_args<Base, Args...>( m_args, std::index_sequence_for<Args...>{} ); }; } // 修改倒数第二个参数(确保类型与原始参数一致) void updatePenultimateArg(FooType new_foo) { if (m_args.size() < 2) { throw std::invalid_argument("参数数量不足,无法修改倒数第二个参数"); } m_args[m_args.size() - 2] = new_foo; } // 创建新的Base实例 Base createBase() const { return m_constructor(); } private: std::vector<std::any> m_args; FooType m_foo; // 存储构造Base的逻辑,通过std::function实现类型擦除 std::function<Base()> m_constructor; };
关键说明
- 类型信息保留:
Child的构造函数通过成员模板捕获原始参数类型Args...,并将对应的构造逻辑存储到m_constructor中,解决了std::any丢失类型信息的问题。 - 参数转换与构造:借助
std::index_sequence和折叠表达式,遍历m_args中的每个元素,用std::any_cast转换为原始类型后传递给Base的构造函数。 - 安全性:修改倒数第二个参数时,确保赋值的
FooType与原始参数类型一致,避免std::bad_any_cast异常;编译期的static_assert会校验参数数量是否匹配。
使用示例
// 构造Child实例 Child<MyBase> child{1, FooType{}, "hello"}; // 修改倒数第二个参数 child.updatePenultimateArg(FooType{new_value}); // 创建新的Base实例 MyBase new_base = child.createBase();
内容的提问来源于stack exchange,提问作者P45 Imminent
相关产品推荐
相关产品推荐

