Извлечение и изменение параметров шаблона - PullRequest
0 голосов
/ 01 декабря 2018

Я написал следующий класс для извлечения параметров шаблона класса 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...>;
};

Я использую его как частьзаводских строительных блоков.С помощью этого класса признаков я могу построить 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>();
}

Код теста здесь .

Я знаю, что это связано с тем фактом, что шаблон с переменными параметрами не может соответствовать сочетанию типов и типов шаблонов.

Есть решение или я ошибаюсь?Я могу использовать до C ++ 14 (без C ++ 17).

Ответы [ 2 ]

0 голосов
/ 01 декабря 2018

Вы - мета-метапрограммирование, и C ++ на самом деле не поддерживает это.Это также сомнительный план, вроде 3-звездочного программирования.

Если вы хотите использовать мета-метапрограмму, вам придется ограничить свое метапрограммирование, чтобы оно было более единообразным.Превратите все в типы.

Используйте 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>>();
0 голосов
/ 01 декабря 2018

Как вы только что знаете (и как объяснил liliscent) параметр шаблона-типа объявления шаблона-шаблона - это разные вещи, поэтому вы не можете передать шаблон-шаблон там, где ожидается параметр типа шаблона.И наоборот.

Я не знаю, что именно вы хотите сделать с вашим derived_type_traits, но лучшее, что я могу себе представить, чтобы решить эту проблему, - это обернуть типы шаблонов в структуруи передайте их как typenames.

В качестве примера ... если вы определите «шаблонную оболочку шаблона» ttw следующим образом

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

, вы можете изменить D case какследует

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