Variadic模板函数,其中返回类型取决于模板参数列表

时间:2016-05-01 21:17:02

标签: c++ templates variadic-templates variadic

我得到“错误的模板参数数量(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 ...> >())

我对这个问题是对的吗?我该如何解决?

非常感谢!

2 个答案:

答案 0 :(得分: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;
    }
};

答案 1 :(得分:1)

似乎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...>> 
{
    /* ... */
}