【问题标题】:Template tail structure raise "incomplete type in nested name specifier"模板尾部结构引发“嵌套名称说明符中的不完整类型”
【发布时间】:2017-10-29 10:13:11
【问题描述】:

我试图操纵 C++14 integer_sequence,但我遇到了一个错误 不明白。让我们从可读性的快捷方式开始

template <typename Int, Int...ints>
using IS = std::integer_sequence<Int, ints...>;

我正在使用 sum 函数减少 IS。我没有为空定义它 故意排序,因为在我使用的实际用例中就是这种情况 gcd、lcm等算术函数:

template<typename ...Int> struct redsum;

template<typename Int, Int i0, Int i1, Int... ints>
struct redsum< IS<Int, i0, i1, ints...> > :
  redsum< IS<Int, i0 + i1, ints...> > { };

template<typename Int, Int i0>
struct redsum<IS<Int, i0> > : std::integral_constant< Int, i0 > { };

现在我想定义尾部(lisp 中的 cdr):

template<typename Int, Int ...ints> struct tail;

template<typename Int, Int i0, Int... ints>
struct tail<Int, i0, ints...> : IS<Int, ints...> { };

template<typename Int, Int... ints>
struct tail<IS<Int, ints...> > : tail<Int, ints...> { };

但这并没有按预期工作:

// This works as expected
static_assert(redsum< IS<unsigned, 2, 5, 12, 18> >::value == 2 + 5 + 12 + 18);

// This doesn't with an incomplete type error
static_assert(redsum< tail < IS<unsigned, 2, 5, 12, 18> > >::value == 5 + 12 + 18);// EDIT

我不明白在这个用例中我的类型在哪里不完整。任何 解释或建议我应该如何写得更好?

【问题讨论】:

  • 如果您使用static_assert(5 + 12 + 18 == redsum&lt;tail&lt;IS&lt;unsigned, 2, 5, 12, 18&gt; &gt; &gt;::value, "") 而不是std::cout,您的意图会更清楚。我猜@max66's answer 是基于误解。
  • @Julius - 我确认:我的回答是基于误解;对不起。
  • @Julius - 添加了另一个答案;希望这不会被误解,
  • @Julius:谢谢你的想法!我正在解决我的问题!

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


【解决方案1】:

您的所有redsum 模板特化都期望tempate 参数的类型为std::integer_sequence。尽管tail 类最终继承自std::integer_sequence,但它本身与特化所期望的类型不匹配,因此编译器回退到(未定义的)主模板。这种情况下的经典方法是在终止递归的类模板特化中定义嵌套类型:

template <typename Int, Int ...ints>
struct tail;

template <typename Int, Int i0, Int... ints>
struct tail<Int, i0, ints...> { using type = IS<Int, ints...>; };
//                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^

template <typename Int, Int... ints>
struct tail<IS<Int, ints...>> : tail<Int, ints...> {};

然后访问嵌套类型得到这个转换的结果:

 redsum< tail < IS<unsigned, 2, 5, 12, 18> >::type >::value
 //                                         ~~~~~^

【讨论】:

  • 好的!我想我或多或少地理解了这个问题。我见过经典的方法,但在我看来它不必要地复杂。现在我知道为什么了。您是否有任何参考资料解释了这场比赛是如何进行的,以及为什么在我的情况下它没有?
【解决方案2】:

Piotr (+1) explained correctly 为什么会出错。

无论如何,如果您只想使用std::integer_sequence 致电tail

tail<IS<long, 2L, 3L, 5L, 7L>>

而不是直接用列表

tail<long, 2L, 3L, 5L, 7L>

你可以强制tail 只接收一个类型名,而不是下面的整数列表

template <typename>
struct tail;

并将想要的类型定义为内部type(如 Piotr 所建议的那样),您可以使用单个特化来实现它

template <typename Int, Int i0, Int... ints>
struct tail<IS<Int, i0, ints...>>
 { using type = IS<Int, ints...>; };

但我建议更通用,不要依赖于 std::integer_sequence 并实现 tail 以使用通用容器

template <template <typename T, T...> class C,
          typename Int, Int i0, Int... ints>
struct tail<C<Int, i0, ints...>>
 { using type = C<Int, ints...>; };

所以如果你定义一个容器为

template <typename T, T...>
struct myList
 { };

满足以下static_assert()

static_assert( std::is_same<tail<myList<int, 2, 3, 5, 7>>::type,
                            myList<int, 3, 5, 7>>{}, "!");

redsum 的建议相同;您可以将其定义为接收单一类型

template <typename>
struct redsum;

并将其泛化以支持通用容器(基于std::ingeger_constant 维护一个value

template <template <typename T, T...> class C, 
          typename Int, Int i0, Int i1, Int... ints>
struct redsum< C<Int, i0, i1, ints...> > :
  redsum< C<Int, i0 + i1, ints...> >
 { };

template <template <typename T, T...> class C, typename Int, Int i0>
struct redsum<C<Int, i0> > : std::integral_constant< Int, i0 >
 { using type = C<Int, i0>; };

这样static_assert()就满足了

static_assert( std::is_same<redsum<myList<int, 2, 3, 5, 7>>::type,
                            myList<int, 17>>{}, "!" );

static_assert( redsum<myList<int, 2, 3, 5, 7>>::value == 17, "!" );

最后...您将此问题标记为 C++14,但我想向您展示使用 C++17 可以使您的 redsum 变得多么简单

template <typename>
struct redsum;

template <template <typename T, T...> class C, typename Int, Int... ints>
struct redsum< C<Int, ints...> >
   : std::integral_constant<Int, (ints + ...)>
 { using type = C<Int, (ints + ...)>; };

【讨论】:

  • +1。 ...有关问题中的方法失败的原因,请参阅this other answer
  • @Julius - 你是对的:Piotr 的回答应该更清楚地说明最初的失败点。答案已修改。
猜你喜欢
  • 1970-01-01
  • 2014-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-26
  • 2021-07-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多