模板参数提取和修改

Template parameter extraction and modification

我写了下面的 class 来提取 Base class 的模板参数并附加到 Derived class 的模板参数在编译时:

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...>;
};

我将它用作工厂积木的一部分。 有了这个特征 class,我可以从一组模板参数和 Base class :

构造一个 Derived
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)。

如您所知(正如 liliscent 所解释的那样)模板模板和模板类型参数是不同的东西,因此您不能在需要模板类型参数的地方传递模板模板。反之亦然。

我不知道你到底想用你的 derived_type_traits 做什么,但我能想象的最好的解决这个问题的方法是将模板模板类型包装在一个结构中并传递它们作为类型名称。

举个例子...如果你定义一个"template template wrapper" 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>();
}

你是元元编程,而 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>>();