模板参数提取与修改

时间:2018-12-01 14:49:34

标签: c++ c++14 variadic-templates template-meta-programming typetraits

我编写了以下类来提取Base类的模板参数,并在编译时将Derived类的模板参数附加到其上:

template <typename...> struct derived_type_traits;

// specialization for the case when Base is not a template
template <typename Base> struct derived_type_traits<Base> {
    template <template<typename...> class Derived, typename... DerivedArgs>
    using type = Derived<DerivedArgs...>;
};

// specialization for the case when Base is a template
template <template <typename...> class Base, typename... BaseArgs> struct derived_type_traits<Base<BaseArgs...>> {
    template <template<typename...> class Derived, typename... DerivedArgs>
    using type = Derived<DerivedArgs..., BaseArgs...>;
};

我将其用作工厂构件的一部分。 有了这个traits类,我可以从一组模板参数和一个Derived类构造一个Base

namespace A {
    class Base {};
    template <typename T>
    class Derived : public Base {};
    auto ptr = new typename derived_type_traits<Base>::type<Derived, int>();
}

namespace B {
    class Base {};
    template <typename T1, typename T2, typename T3>
    class Derived : public Base {};
    auto ptr = new typename derived_type_traits<Base>::type<Derived, int, double, std::string>();    
}

namespace C {
    template <typename T>
    class Base {};
    template <typename T1, typename T2, typename T3, typename T>
    class Derived : public Base<T> {};
    auto ptr = new typename derived_type_traits<Base<int>>::type<Derived, int, double, std::string>();  
}

但是,在以下情况下它不起作用:

namespace D {
    template <typename T>
    class Base {};
    template <typename T1, typename T2, template <typename,typename> class T3, typename T>
    class Derived : public Base<T> {};
    template <typename T1, typename T2> struct Foo {};
    auto ptr = new typename derived_type_traits<Base<int>>::type<Derived, int, double, Foo>();
}

namespace E {
    template <typename U1, template <typename,typename> class U2>
    class Base {};
    template <typename T1, typename T2, typename T3, typename U1, template<typename,typename> class U2>
    class Derived : public Base<U1,U2> {};
    template <typename T1, typename T2> struct Foo {};
    auto ptr = new typename derived_type_traits<Base<int, Foo>>::type<Derived, int, double, std::string>();
}

测试代码为here

我知道这与可变参数模板不能匹配类型和模板类型的混合的事实有关。

是否有解决方案,还是我走错了路?我最多可以使用C ++ 14(没有C ++ 17)。

2 个答案:

答案 0 :(得分:1)

您是元元编程,而C ++确实不支持。这也是一个有问题的计划,有点像三星级的编程。

如果您要使用元元程序,则必须限制元编程的统一性。将所有内容变成类型。

使用std::integral_constant类型来传递值(甚至可以在函数指针上使用!)。使用

template<template<class...>class Z>struct ztemplate

传递模板。

或者(或另外)转向基于值的元编程。将类型作为template<class>struct tag_t值传递,模板被替换为从标签到标签等的对象或函数。

这两种方法都可以在更高程度上简化和递归元编程。

在原始C ++ TMP中执行此操作会遇到这个问题,同时还有一个令人讨厌的规则,这些规则最终会使您陷入困境(例如将包传递给1个参数模板)和中等的编译器支持。

最后,请注意您的具体情况:

template <typename T1, typename T2, typename T3, typename U1, template<typename,typename> class U2>
class Derived : public Base<U1,U2> {};

比:笨拙

template <typename T1, typename T2, typename T3, class Base>
class Derived : public Base {};
auto ptr = new Derived< int, double, std::string, Base<int, Foo>>();

答案 1 :(得分:0)

正如您所知道的(并由liliscent解释),template-template ad模板类型参数是不同的东西,因此您不能在期望模板类型参数的地方传递模板模板。反之亦然。

我不知道您想对您的derived_type_traits做些什么,但是为了解决这个问题,我能想到的最好的办法是将模板模板类型包装在结构中,并将它们作为类型名称。

通过示例...如果您按如下方式定义“模板模板包装器” ttw

template <template <typename...> class>
struct ttw  // template template wrapper
 { };

您可以按以下方式修改D的情况

namespace D
 {
   template <typename T>
   class Base
    { };

   // now T3 is a typename !
   template <typename T1, typename T2, typename T3, typename T>
   class Derived : public Base<T>
    { };

   template <typename T1, typename T2>
   struct Foo
    { };

   auto ptr = new typename derived_type_traits<Base<int>>::type<
      Derived, int, double, ttw<Foo>>();
   // ----------------------^^^^^^^^   and here Foo is wrapped
}

对于案例E,我能想象的最好的是

namespace E
 {
   // now U2 is a typename !
   template <typename U1, typename U2>
   class Base
    { };

   template <typename T1>
   struct Foo
    { };

   // U2 is a typename also here
   template <typename T1, typename T2, typename T3, typename U1, typename U2>
   class Derived : public Base<U1, U2>
    { };

   //  and Foo is wrapped again -------------------------VVVVVVVV        
   auto ptr = new typename derived_type_traits<Base<int, ttw<Foo>>>::type<
      Derived, int, double, std::string>();
}