嵌套类的访问变体:如何避免C++代码冗余?
问题:精简多态类配置实例化的冗余代码
我有大量继承自Base的多态类,每个类都包含名为Configuration的嵌套结构体,用于存储该类的全部超参数。针对这个类层级,我用std::variant存储所有可能的配置变体,并提供instantiate函数从配置生成实例。原始实现如下:
#include <variant> #include <memory> class Base {}; class A : public Base { public: struct Configuration { /*超参数*/ }; A(Configuration const&) {} }; class B : public Base { public: struct Configuration { /*超参数*/ }; B(Configuration const&) {} }; struct BaseConfiguration { using type = std::variant<A::Configuration, B::Configuration>; static std::unique_ptr<Base> instantiate(type const& config); }; struct BaseVisitor { // 这里存在大量重复逻辑 std::unique_ptr<Base> operator()(A::Configuration const& config) { return std::make_unique<A>(config); } std::unique_ptr<Base> operator()(B::Configuration const& config) { return std::make_unique<B>(config); } }; std::unique_ptr<Base> BaseConfiguration::instantiate(type const& config) { return std::visit(BaseVisitor(), config); } int main() { BaseConfiguration::type config = A::Configuration(); std::unique_ptr<Base> x = BaseConfiguration::instantiate(config); return 0; }
BaseVisitor中的每个operator()逻辑完全一致,但类数量多时代码冗余严重。我尝试用模板精简,但C++17环境下编译失败:
struct BaseVisitor { template <typename X> std::unique_ptr<Base> operator()(typename X::Configuration const& config) { return std::make_unique<X>(config); } };
编译器错误:
<source>: In static member function 'static std::unique_ptr<Base> BaseConfiguration::instantiate(const type&)': <source>:36:22: error: no matching function for call to 'visit(BaseVisitor, const BaseConfiguration::type&)' 36 | return std::visit(BaseVisitor(), config); | ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
原因分析
原模板方案失败的核心原因是:typename X::Configuration属于非推导上下文,编译器无法从传入的A::Configuration或B::Configuration参数反向推导出模板参数X,导致std::visit无法找到匹配的重载。
解决方案
方案一:给Configuration添加类型别名
在每个类的Configuration中添加指向所属类的类型别名,让Visitor可以直接通过配置类型获取目标类:
#include <variant> #include <memory> class Base {}; class A : public Base { public: struct Configuration { using TargetType = A; // 添加类型别名 /*超参数*/ }; A(Configuration const&) {} }; class B : public Base { public: struct Configuration { using TargetType = B; // 添加类型别名 /*超参数*/ }; B(Configuration const&) {} }; struct BaseConfiguration { using type = std::variant<A::Configuration, B::Configuration>; static std::unique_ptr<Base> instantiate(type const& config); }; struct BaseVisitor { // 单一模板处理所有配置类型 template <typename Config> std::unique_ptr<Base> operator()(Config const& config) { return std::make_unique<typename Config::TargetType>(config); } }; std::unique_ptr<Base> BaseConfiguration::instantiate(type const& config) { return std::visit(BaseVisitor(), config); } int main() { BaseConfiguration::type config = A::Configuration(); std::unique_ptr<Base> x = BaseConfiguration::instantiate(config); return 0; }
方案二:使用类型 trait 映射配置与类
如果不想修改Configuration结构体,可以用类型 trait 模板实现配置到目标类的映射:
#include <variant> #include <memory> class Base {}; class A : public Base { public: struct Configuration { /*超参数*/ }; A(Configuration const&) {} }; class B : public Base { public: struct Configuration { /*超参数*/ }; B(Configuration const&) {} }; // 定义类型 trait template <typename Config> struct ConfigToTarget; // 特化每个配置对应的目标类 template <> struct ConfigToTarget<A::Configuration> { using type = A; }; template <> struct ConfigToTarget<B::Configuration> { using type = B; }; // 简化 trait 调用的别名 template <typename Config> using ConfigToTarget_t = typename ConfigToTarget<Config>::type; struct BaseConfiguration { using type = std::variant<A::Configuration, B::Configuration>; static std::unique_ptr<Base> instantiate(type const& config); }; struct BaseVisitor { template <typename Config> std::unique_ptr<Base> operator()(Config const& config) { return std::make_unique<ConfigToTarget_t<Config>>(config); } }; std::unique_ptr<Base> BaseConfiguration::instantiate(type const& config) { return std::visit(BaseVisitor(), config); } int main() { BaseConfiguration::type config = A::Configuration(); std::unique_ptr<Base> x = BaseConfiguration::instantiate(config); return 0; }
内容的提问来源于stack exchange,提问作者Caduchon
相关产品推荐
相关产品推荐

