【问题标题】:Variadic class template argument reduction vs variadic function template argument reduction可变参数类模板参数缩减与可变参数函数模板参数缩减
【发布时间】:2021-02-17 21:37:52
【问题描述】:

可变参数类模板参数推导与可变参数函数模板参数推导有何不同?我目前有一个事件对象,它创建一个作为某个基类的子类的对象,并且每个子类的构造函数都采用不重叠的类型和参数数量。我必须完成的课程看起来像这样

template <class T, class... Args>
    struct CreateDrawableObjTreeEvent : RenderEvent {
        CreateDrawableObjTreeEvent(Args&&... args) {
            drawable = new T(std::forward<Args>(args)...);
        }

        T* drawable;
    };

我想要实例化它的方式看起来像这样

RenderEvent* test = new CreateDrawableObjTreeEvent<DrawableObject>("testvertices");

其中 DrawableObject 构造函数采用 1 个字符串。当我尝试运行它时,我遇到了这个错误

'CreateDrawableObjTreeEvent<DrawableObject>::CreateDrawableObjTreeEvent': no overloaded function takes 1 argument   

但是,当我修改类结构以便实例化发生在函数中而不是构造函数中时,它编译时不会出现问题

    template <class T>
    struct CreateDrawableObjTreeEvent : RenderEvent {
        CreateDrawableObjTreeEvent() {}

        template <class... Args>
        void instantiate(Args&&... args) {
            drawable = new T(std::forward<Args>(args)...);
        }

        T* drawable;
    };

...


CreateDrawableObjTreeEvent<DrawableObject>* test = new CreateDrawableObjTreeEvent<DrawableObject>();
test->instantiate("testvertices");

我真的很想能够使用第一种方法的语法,有什么方法可以干净地实现这种行为吗?

【问题讨论】:

  • Args&amp;... args 中的单个 & 符号 &amp; 是错字吗?为了完美转发,通常写Args&amp;&amp;... args 以便参考折叠规则根据需要生效。

标签: c++ templates variadic-templates variadic template-argument-deduction


【解决方案1】:

你可以得到

RenderEvent* test = new CreateDrawableObjTreeEvent<DrawableObject>("testvertices");

通过将类模板的可变部分移动到构造函数的模板来工作,例如

template <class T>
struct CreateDrawableObjTreeEvent : RenderEvent {
    template <class... Args>
    CreateDrawableObjTreeEvent(Args&&... args) :
        drawable(new T(std::forward<Args>(args)...))  {}

    T* drawable;
};

我应该注意的是,如果您尝试复制 CreateDrawableObjTreeEvent 的左值,则将调用构造函数,因为生成的函数会更好地匹配。要停止这种情况,您需要向模板添加约束,或者您可以添加一个构造函数,如

CreateDrawableObjTreeEvent(CreateDrawableObjTreeEvent& other) : 
    CreateDrawableObjTreeEvent(static_cast<const CreateDrawableObjTreeEvent&>(other)) {}
    

这将是更好的匹配,因为它不是模板。

【讨论】:

    【解决方案2】:

    如果你可以使用 C++17,那么 CTAD (Class Template Argument Deduction) 你也可以推断出第一个模板参数 (T),通过某种类型的包装器传递它。

    我的意思是......假设你写了一些东西

    template <typename>
    struct wrap_t
     { };
    
    template <typename T, typename... Args>
    struct CreateDrawableObjectEvent : public RenderEvent
     {
       // ........................VVVVVVVVV
       CreateDrawableObjectEvent (wrap_t<T>, Args && ... args)
          : drawable{ new T{std::forward<Args>(args)...} }
        { }
    
       std::unique_ptr<T> drawable;
     };
    

    你可以创建test传递如下

    std::unique_ptr<RenderEvent>
       test { new CreateDrawableObjectEvent{wrap_t<DrawableObject>{}, "testvertices"} };
    // .....................................^^^^^^^^^^^^^^^^^^^^^^^^
    

    只需使用从 CreateDrawableObjectEvent 构造函数生成的隐式 CTAD。

    以下是完整的编译示例

    #include <memory>
    
    struct RenderEvent
     { };
    
    struct DrawableObject
     {
       template <typename ... Args>
       DrawableObject (Args...)
        { }
     };
    
    template <typename>
    struct wrap_t
     { };
    
    template <typename T, typename... Args>
    struct CreateDrawableObjectEvent : public RenderEvent
     {
       CreateDrawableObjectEvent (wrap_t<T>, Args && ... args)
          : drawable{ new T{std::forward<Args>(args)...} }
        { }
    
       std::unique_ptr<T> drawable;
     };
    
    int main ()
     {
       std::unique_ptr<RenderEvent>
          test { new CreateDrawableObjectEvent{wrap_t<DrawableObject>{}, "testvertices"} };
     }
    

    未经请求的建议:当您直接分配内存时(new/new[]),请(拜托!)避免直接管理,而是使用智能指针作为std::unique_ptr/std::shared_prt/etc .

    【讨论】:

      猜你喜欢
      • 2016-12-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-01
      • 2018-10-25
      • 2013-09-22
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      相关资源
      最近更新 更多