首页 文章

使用可变参数模板参数丢失rvalue限定符

提问于
浏览
3

我正在尝试编写一个通用工厂类,为我的应用程序自动注册类型 .

为了保证灵活性,这个工厂有一个构造函数参数的可变参数模板参数;即它允许默认构造函数或构造函数需要任意数量的参数 . 参数名称相当不言自明; AbstractType是工厂的抽象基类;返回的对象将是此类型的 std::shared_ptr .

这个通用工厂工作正常,我为它编写的所有测试工作都非常好,直到我试图为包含不允许复制构造或赋值的类(作为数据成员)的特定类层次结构创建工厂 . 我试图通过使用模板参数的右值引用来解决这个问题;但是,这不符合我的预期 . 具体来说,如果我将工厂实例定义为采用 A&& 类型的构造函数参数,则会失败并显示错误,告诉我没有从A转换为A && .

在此示例中, My_AbstractData_ContextOther_Class 在其他地方声明 . 如上所述,这里的想法是具体类型 CT 将具有带签名的构造函数:

class CT {
    CT(Data_Context&&, Other_Class const&);
/* ... */
};

class My_Abstract; // forward declaration

template <class ConcreteType>
using My_Factory_Registrar =
    Factory_Registrar<ConcreteType, My_Abstract, Data_Context &&, Other_Class const&>;
using My_Factory =
    Generic_Factory<My_Abstract, Data_Context &&, Other_Class const&>;

也许我在这里缺少一些基本的东西,但是当我修改代码时:

template <class ConcreteType>
using My_Factory_Registrar =
    Factory_Registrar<ConcreteType, My_Abstract, Data_Context const&, Other_Class const&>;
using My_Factory =
    Generic_Factory<ConcreteType, Data_Context const&, Other_Class const&>;

然后一切都编译并正常工作 . 我很清楚r值可以用于const引用参数,所以我并不感到困惑,为什么这有效,因为我完全混淆为什么第一个代码片段不起作用 . 几乎看起来在可变参数模板扩展过程中删除了右值参考限定符 .

我不确定它是否有助于澄清事物,但工厂类本身的代码如下:

template <class AbstractType, class...ConstructorArgs>    
class Generic_Factory{

    public:
        static std::shared_ptr<AbstractType> Construct(std::string key, ConstructorArgs... arguments){
            auto it = Get_Registry()->find(key);
            if (it == Get_Registry()->cend())
                return nullptr;

            auto constructor = it->second;
            return constructor(arguments...);
        }

        using Constructor_t = std::function<std::shared_ptr<AbstractType>(ConstructorArgs...)>;
        using Registry_t = std::map< std::string, Constructor_t>;

        Generic_Factory(Generic_Factory const&) = delete;
        Generic_Factory& operator=(Generic_Factory const&) = delete;

    protected:
        Generic_Factory(){}
        static Registry_t* Get_Registry();

    private:
        static Registry_t* _registry_;

    };

    template <class ConcreteType, class AbstractType, class...ConstructorArgs>
struct Factory_Registrar : private Generic_Factory<AbstractType, ConstructorArgs...>{
        using Factory = Generic_Factory<AbstractType, ConstructorArgs...>;
        using Constructor_t = typename Factory::Constructor_t;

public:
        Factory_Registrar(std::string const& designator, Constructor_t  object_constructor){
            auto registry = Factory::Get_Registry();
            if (registry->find(designator) == registry->cend())
                registry->insert(std::make_pair(designator, object_constructor));
        }
    };

谢谢你的帮助 .

什穆埃尔

2 回答

  • 3

    Perfect Forwarding 旨在用于这些情况 . 你的代码很长 . 我使用简化版的 make_unique 进行演示 .

    template <typename T, typename ...Args>
    auto make_unique(Args&&... args) -> std::unique_ptr<T>
    {
        return std::unique_ptr<T>{new T(std::forward<Args>(args)...)};
    }
    
  • 1

    为了能够在不丢失类型信息的情况下转发rvalue输入参数,您需要使用通用引用 . 典型的例子是:

    template<class T>
    void Forwarder(T&& t)
    {
    Func(std::forward<T>(t));
    }
    

    这样,就不会丢失类型信息,并且会调用Func的正确重载 .

    另一方面,如果 Forwarder 的主体调用 Func (t),则只有Func的左值超载才匹配 .

相关问题