【问题标题】:Variadic template function where return type depends on template argument list返回类型取决于模板参数列表的可变参数模板函数
【发布时间】:2016-05-01 21:17:01
【问题描述】:

我收到“模板参数数量错误(2,应该是 1)”的错误,我无法真正理解。

我有一个类,它为想要与之交互的其他类型提供一些帮助函数,设置第一个模板参数,它们必须在创建时自动同意相互兼容。为了以一种方便、通用的方式执行此操作,我决定使用可变参数模板,该模板旨在传递构造函数参数和要创建的对象类型采用的其他模板参数:

template<typename INTERNAL_TYPE>
class Linker
{
  template< template<typename, typename ...> class INPUT_OBJ_TYPE, class ... TEMPLATE_ARGS, class ... CONSTRUCTOR_ARGS >
  std::shared_ptr< INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS ...> > getLinked( CONSTRUCTOR_ARGS ... args )
  {
    std::shared_ptr< INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS ...> > ptr = std::make_shared< INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS ...> >( args... );
    return ptr;
  }
};

这适用于以下课程:

template<typename INTERNAL_TYPE, typename SECOND_TYPE>
class TEST_CLASS_1
{};

也就是说,我可以做到以下几点:

Linker<int> container;
auto test_1 = container.getLinked<TEST_CLASS_1,double>();

然后我尝试对另一个仅在一个参数上模板化的类做同样的事情:

template<typename INTERNAL_TYPE>
class TEST_CLASS_2
{};
auto test_2 = container.getLinked<TEST_CLASS_2>();

但是得到上面提到的错误..为什么?如果我从函数声明/定义中删除 TEMPLATE_ARGS ,我想尽可能多地为第二个测试编译代码(虽然第一个测试不再)。所以我认为当时的编译器还没有意识到 TEMPLATE_ARGS 对于第二个测试是空的,并且由于模板参数太多而引发错误。所以我想我可能需要使用尾随返回类型,例如

template< template<typename, typename ...> class INPUT_OBJ_TYPE,
          class ... TEMPLATE_ARGS, class ... CONSTRUCTOR_ARGS >
auto getLinked( CONSTRUCTOR_ARGS ... args )
    -> std::shared_ptr< INPUT_OBJ_TYPE<TREE_TYPE,TEMPLATE_ARGS ...> >

或使用decltype,但这也不起作用:

template< template<typename, typename ...> class INPUT_OBJ_TYPE,
          class ... TEMPLATE_ARGS, class ... CONSTRUCTOR_ARGS >
auto getLinked( CONSTRUCTOR_ARGS ... args )
    ->decltype(std::shared_ptr< INPUT_OBJ_TYPE<TREE_TYPE,TEMPLATE_ARGS ...> >())

我对这个问题是否正确?我该如何解决?

非常感谢!

【问题讨论】:

    标签: c++ templates variadic-templates variadic


    【解决方案1】:

    这可能是编译器错误。

    获得你想要的东西的一种方法是提供两个函数,你的原始函数和另一个没有不可演绎参数包的函数:

    template<typename INTERNAL_TYPE>
    struct Linker
    {
        template<
            template<typename, typename ...> class INPUT_OBJ_TYPE,
            class ... TEMPLATE_ARGS,
            class ... CONSTRUCTOR_ARGS
        >
        std::shared_ptr<
            INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS...>
        > getLinked( CONSTRUCTOR_ARGS ... args )
        {
            std::shared_ptr< INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS...> > ptr =
              std::make_shared< INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS...> >( args... );
            return ptr;
        }
    
        template<
            template<typename, typename ...> class INPUT_OBJ_TYPE,
            class ... CONSTRUCTOR_ARGS
        >
        std::shared_ptr<
            INPUT_OBJ_TYPE<INTERNAL_TYPE>
        > getLinked( CONSTRUCTOR_ARGS ... args )
        {
            std::shared_ptr< INPUT_OBJ_TYPE<INTERNAL_TYPE> > ptr =
              std::make_shared< INPUT_OBJ_TYPE<INTERNAL_TYPE> >( args...);
            return ptr;
        }
    };
    

    【讨论】:

    • 未推导的参数包被推导为空包。
    • @T.C.:那只是用于尾随参数包,不是吗?
    • @VaughnCato 这就是我目前正在做的事情。我很惊讶编译器无法推断出,因为只给出了 INPUT_OBJ_TYPE,所以以下两个参数包都必须为空。有什么我看不到的原因吗?只有一个功能会更好......
    • @bluewater2:这是一个奇怪的案例。我认为可以修改标准以支持您想要的。目前它只允许推导尾随参数包,但似乎可以更改为允许将最后一个未推导的参数包推导为空。
    • 我不太相信“尾随”具有您赋予它的含义(template&lt;class T, class U&gt; void f(T t, U u);;f(0, 1) 可以,T 算作“尾随”-规则是“可以从显式模板参数列表中省略可以推导([temp.deduct])或从默认模板参数获得的尾随模板参数。”),我不相信这是错误的原因OP也看到了;如果你保留 TEMPLATE_ARGS 包,只是不在返回类型中使用它,它就会编译。
    【解决方案2】:

    看来TEST_CLASS_2 的初始替换是针对编译器的“每个有效的特化都需要一个空包”代码路径——也就是说,以下模板格式错误,不需要诊断:

    template<class... TArgs, class... CArgs>
    std::shared_ptr<TEST_CLASS_2<INTERNAL_TYPE, TArgs...>> getLinked( CArgs... args )
    {
       /* ... */
    }
    

    因为它的每个有效特化都要求TArgs 是一个空包。对getLinked 的初始替换确实会产生类似的结果,但是当然,您实际上并没有编写该模板,所以我不相信应该有错误,但无论如何。

    一种可能的解决方法是将INPUT_OBJ_TYPE 的替换推迟到推断之后,方法是采用标记类型并推断所有内容:

    template< template<class, class...> class, class... > struct tag {};
    
    template< template<class, class...> class INPUT_OBJ_TYPE,
              class... TArgs, class... CArgs >
    auto getLinked(tag<INPUT_OBJ_TYPE, TArgs...>, CArgs... args )
        -> std::shared_ptr<INPUT_OBJ_TYPE<INTERNAL_TYPE,TArgs...>> 
    {
        /* ... */
    }
    

    【讨论】:

      猜你喜欢
      • 2012-07-02
      • 2022-01-09
      • 2018-04-09
      • 1970-01-01
      • 2020-11-16
      • 2021-05-23
      • 1970-01-01
      • 2021-10-01
      • 1970-01-01
      相关资源
      最近更新 更多