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

同一类多构造函数的工厂模式注册重定义问题求解

支持多构造函数的智能指针工厂模式改造

问题分析

当前代码存在两个核心问题:

  1. 静态变量重复定义:DerivedExample中仅声明了一个static bool sign,却尝试两次定义它,触发编译错误。
  2. 注册表键冲突:工厂仅用类名字符串作为唯一键,两个create函数注册时会互相覆盖,导致调用带参数的instantiate时无法找到正确的函数指针。

修改后的完整代码

#include <unordered_map>
#include <string>
#include <iostream>
#include <memory>
#include <typeindex>
#include <tuple>

class Base;

class myFactory
{
public:
    // 用类名+参数类型组合作为注册表的键,避免不同参数构造函数的键冲突
    typedef std::pair<std::string, std::type_index> KeyType;
    typedef std::unordered_map<KeyType, void*, 
        std::hash<KeyType>> registry_map;

    virtual ~myFactory() = default;

    static registry_map& registry()
    {
        static registry_map impl;
        return impl;
    }

    template<typename... T>
    static std::shared_ptr<Base> instantiate(const std::string& name, T&&... args)
    {
        // 生成包含类名和参数类型元组的键
        KeyType key{name, std::type_index(typeid(std::tuple<T...>))};
        auto it = registry().find(key);
        if (it == registry().end()) return nullptr;
        
        typedef std::shared_ptr<Base> (*create_type)(T...);
        auto create_fun = reinterpret_cast<create_type>(it->second);
        return create_fun(std::forward<T>(args)...);
    }

    template<typename F, typename... T>
    static bool sign(const std::string& name, F func)
    {
        // 根据注册函数的参数类型生成对应键
        KeyType key{name, std::type_index(typeid(std::tuple<T...>))};
        registry()[key] = reinterpret_cast<void*>(func);
        return true;
    }
};

class Base : public myFactory
{
public:
    virtual void f() = 0;
    virtual ~Base() = default;
};

class DerivedExample : public Base
{
private:
    // 为每个构造函数注册项定义独立的静态变量,避免重复定义
    static bool sign_default;
    static bool sign_two_int;

public:
    DerivedExample(int a, int b) { std::cout << "Construct with " << a << " and " << b << std::endl; }
    DerivedExample() = default;

    static std::shared_ptr<Base> create() { return std::make_shared<DerivedExample>(); }
    static std::shared_ptr<Base> create(int a, int b) { return std::make_shared<DerivedExample>(a, b); }

    virtual void f() override { std::cout << "DerivedExample" << std::endl; }
};

// 注册无参构造版本
bool DerivedExample::sign_default = DerivedExample::myFactory::sign<decltype(&DerivedExample::create), void>("DerivedExample", &DerivedExample::create);
// 注册带两个int参数的版本
bool DerivedExample::sign_two_int = DerivedExample::myFactory::sign<decltype(&DerivedExample::create(int, int)), int, int>("DerivedExample", &DerivedExample::create);

int main()
{
    std::shared_ptr<Base> p1 = Base::instantiate("DerivedExample");
    std::shared_ptr<Base> p2 = Base::instantiate("DerivedExample", 1, 2);
    
    p1->f();
    p2->f();

    return 0;
}

关键改动说明

  • 注册表键优化:将原单一字符串键改为std::pair<std::string, std::type_index>,用std::type_index记录参数类型元组的信息,确保不同参数的构造函数注册到独立条目,不会互相覆盖。
  • 解决静态变量冲突:为每个注册的构造函数定义独立的静态变量(sign_default和sign_two_int),避免重复定义编译错误。
  • 完美转发参数:在instantiate函数中使用std::forward<T>(args)...,确保参数传递的正确性,尤其是处理右值引用场景。
  • 明确注册类型:调用sign时显式指定模板参数,让工厂能正确生成对应参数类型的键。

简化替代方案

如果不需要自动识别参数类型,也可以直接用不同的注册名区分构造函数,实现更简单:

// 注册时使用不同的名字
bool DerivedExample::sign_default = DerivedExample::myFactory::sign("DerivedExample", &DerivedExample::create);
bool DerivedExample::sign_two_int = DerivedExample::myFactory::sign("DerivedExample_int_int", &DerivedExample::create);

// 实例化时对应不同的注册名
std::shared_ptr<Base> p2 = Base::instantiate("DerivedExample_int_int", 1, 2);

这种方式需要手动维护注册名和参数的对应关系,适合简单场景。

内容的提问来源于stack exchange,提问作者user20995624

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 03:12:03