реализация списка типов в C ++ - PullRequest
0 голосов
/ 22 апреля 2020

Я изучаю метапрограммирование в C ++ с помощью шаблонов и пытаюсь реализовать список типов и операции над ним.

Я определяю список типов как шаблон класса variadi c и операции как шаблонные структуры с частичной специализацией. Такие операции, как Front, PopFront и PushFront работают нормально, но когда я создаю экземпляр Back and Element (чтобы проиндексировать список типов для получения n-го элемента), компилятор жалуется, что я использую неполный тип:

/**** typelist ****/
template <typename... Types>
struct Typelist
{
};

/**** get first element ****/
template <typename List>
struct Front;

template <typename Head, typename... Tail>
struct Front<Typelist<Head,Tail...>>
{
    typedef Head type;
};

template <typename List>
using FrontT = typename Front<List>::type;

/**** pop first element ***/
template <typename List>
struct PopFront;

template <typename Head, typename... Tail>
struct PopFront<Typelist<Head,Tail...>>
{
    using type = Typelist<Tail...>;
};

template <typename List>
using PopFrontT = typename PopFront<List>::type;

/**** push first element ****/
template <typename List, typename Element>
struct PushFront;

template <typename... Elements, typename Element>
struct PushFront<Typelist<Elements...>,Element>
{
    using type = Typelist<Element,Elements...>;
};

template <typename List, typename Element>
using PushFrontT = typename PushFront<List,Element>::type;

/**** get last element ****/ 
template <typename List>
struct Back;

template <typename... Head, typename Tail>
struct Back<Typelist<Head...,Tail>>
{
    typedef Tail type;
};

/**** indexing ****/
template <typename List, unsigned Index>           // recursive case
struct Element
{
    using type = typename Element<typename PopFront<List>::type, Index - 1>::type;
};

template <typename List>
struct Element<List,0>                             // base case
{
    typedef typename Front<List>::type type;
};

// template <typename... Types>                        // base case
// struct Element<Typelist<Types...>,0>
// {
//     using type = typename Front<Typelist<Types...>>::type;
// };

// template <typename... Types, unsigned Index>        // recursive case
// struct Element<Typelist<Types...>,Index>
// {
//     using type = typename Element<typename PopFront<Typelist<Types...>>::type, Index - 1>::type;
// };

template <typename List, unsigned Index>
struct ElementI : ElementI<PopFrontT<List>,Index - 1>
{
};

template <typename List>
struct ElementI<List,0> : Front<List>
{
};

template <typename List, unsigned Index>
using ElementT = typename Element<List,Index>::type;

Я понимаю, что могу использовать пакеты параметров шаблона для любого параметра в частичной специализации, если можно выводить аргументы, поэтому я думаю, что объявление правильное, верно?

РЕДАКТИРОВАТЬ Элемент сейчас работает, я допустил орфографическую ошибку, вызвав его, Back по-прежнему нет, и я не могу понять, почему.

EDIT это код для проверки списка типов и компилятора (G CC 7.2) ошибка (я немного изменил реализацию списка типов):

РЕДАКТИРОВАТЬ, РЕДАКТИРОВАТЬ компилятор G CC 7.2

#include "typelist.hpp"
#include <type_traits>

int main(int argc, char **argv)
{
    Typelist<int, double, bool> tl;

    static_assert(std::is_same<typename Front<decltype(tl)>::type,int>::value, "not same");
    static_assert(std::is_same<typename PopFront<decltype(tl)>::type,Typelist<double,bool>>::value, "not same");
    static_assert(std::is_same<typename PushFront<decltype(tl),float>::type,Typelist<float,int,double,bool>>::value, "not same");
    /* compiler error */ static_assert(std::is_same<typename Back<decltype(tl)>::type,bool>::value, "not same");
    static_assert(std::is_same<typename ElementI<decltype(tl),0>::type,int>::value, "not same");
    static_assert(std::is_same<typename ElementI<decltype(tl),1>::type,double>::value, "not same");
    static_assert(std::is_same<ElementT<decltype(tl),2>,bool>::value, "not same");

    return 0;
}


main.cpp: In function 'int main(int, char**)':
main.cpp:11:61: error: invalid use of incomplete type 'struct Back<Typelist<int, double, bool> >'
     static_assert(std::is_same<typename Back<decltype(tl)>::type,bool>::value, "not same");
                                                             ^~~~
In file included from main.cpp:1:0:
typelist.hpp:48:8: note: declaration of 'struct Back<Typelist<int, double, bool> >'
 struct Back;
        ^~~~
main.cpp:11:70: error: template argument 1 is invalid
     static_assert(std::is_same<typename Back<decltype(tl)>::type,bool>::value, "not same");

1 Ответ

0 голосов
/ 22 апреля 2020

С clang++ (Apple Clang версии 11.0.3 (clang-1103.0.32.59)) Я получаю следующее сообщение:

t.cpp:51:8: error: class template partial specialization contains template parameters that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization]
struct Back<Typelist<Head...,Tail>>
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
t.cpp:50:23: note: non-deducible template parameter 'Head'
template <typename... Head, typename Tail>
                      ^
t.cpp:50:38: note: non-deducible template parameter 'Tail'
template <typename... Head, typename Tail>
                                     ^

От: https://en.cppreference.com/w/cpp/language/parameter_pack (извините у меня нет основной ссылки)

В шаблоне первичного класса пакет параметров шаблона должен быть последним параметром в списке параметров шаблона.
В шаблоне функции параметр шаблона Пакет может появиться раньше в списке при условии, что все следующие параметры могут быть выведены из аргументов функции или имеют аргументы по умолчанию:

template<typename... Ts, typename U> struct Invalid; // Error: Ts.. not at the end

template<typename ...Ts, typename U, typename=void>
void valid(U, Ts...);     // OK: can deduce U
// void valid(Ts..., U);  // Can't be used: Ts... is a non-deduced context in this position

valid(1.0, 1, 2, 3);      // OK: deduces U as double, Ts as {int,int,int}

Но мы можем решить эту проблему, определив Back в терминах из Element.

/**** get last element ****/
template <typename List>
struct Back;

template <typename... Args>
struct Back<Typelist<Args...>>
{
    using type = typename Element<Typelist<Args...>, sizeof...(Args) - 1>::type;
};

Вопрос:

Почему у вас есть template using операторы для всех ваших типов, кроме Back и Element?

...