【问题标题】:implementing a typelist in C++在 C++ 中实现类型列表
【发布时间】:2020-08-05 07:27:34
【问题描述】:

我正在使用模板研究 C++ 中的元编程,并且我正在尝试实现一个类型列表及其上的操作。

我将类型列表定义为可变参数类模板,并将操作定义为具有部分特化的模板结构。 Front、PopFront 和 PushFront 等操作可以正常工作,但是当我实例化 Back 和 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;

我知道我可以将模板参数包用于偏特化中的任何参数,只要可以推导出参数,所以我认为声明是正确的,对吧?

编辑 Element 现在可以工作了,我在调用它时犯了拼写错误,Back 仍然没有,我不明白为什么。

编辑 这是测试typelist和编译器(GCC 7.2)错误的代码(我稍微改变了typelist的实现):

EDIT EDIT 编译器是 GCC 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");

【问题讨论】:

  • 请粘贴您遇到的编译错误以及使用哪个编译器。

标签: c++ list templates metaprogramming


【解决方案1】:

使用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;
};

问题:

为什么除了BackElement 之外的所有类型都有template using 语句?

【讨论】:

  • 这只是一个学习 typelists 和 tmp 的练习,所以有些东西可能会丢失。为什么它是一个非演绎的上下文?它总是可以使用除最后一个以外的所有参数来推断包。与 typelist 参数相同
  • @Luca 我不知道这是事实,所以这是猜测。在构建编译器时,您尝试将其构建为尽可能接近 LALR(1),因为这样更容易编写。这意味着您宁愿在词法输入流中最多只查看一个标记来选择要扩展的语法。如果您允许元组扩展包成为 &lt;Head...,Tail&gt; 的头部,您将无法告诉在哪里停止 Head 的语法表达式而不期待 3 个标记(如果您扩展到其余部分,这可能会变得任意复杂语法)。
  • 因此,参数包扩展必须位于语法表达式的末尾,以保持 1 个词位向前(或尽可能接近)。现在 C++ 已经是一种非常复杂的解析语言,因此在添加此功能时,他们刚刚决定我们不希望允许任意复杂的语法(尤其是会破坏编译时间的东西)。那么你在哪里切断语法?你会在打包后以零/一个表达式切断它吗?或者您是否允许打包后的任意数量的表达式。我认为这是一个将编译器复杂性降到最低的设计决策。
  • 我只是想知道这是否确实是原因,解析器很贪婪。
猜你喜欢
  • 1970-01-01
  • 2014-07-27
  • 2015-08-27
  • 1970-01-01
  • 1970-01-01
  • 2012-04-16
  • 2015-02-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多