【问题标题】:Variadic template errors in struct template but not in function template结构模板中的可变模板错误,但函数模板中没有
【发布时间】:2021-10-11 17:37:29
【问题描述】:

为什么这段代码不起作用?

template <size_t counter, typename TTag, typename Head, typename... Tails>
struct Tag2IDImpl {
  constexpr static int value = std::is_same_v<TTag, Head>? counter : Tag2IDImpl<counter+1, TTag, Tails...>::value;
};

template <typename TTag, typename... TParameters>
struct Tag2ID {
  constexpr static int value = Tag2IDImpl<0, TTag, TParameters...>::value;
};

struct A;
struct B;
struct C;
struct D;


int main() {
  std::cout<<Tag2ID<A, B, C, D,A>::value<<std::endl;
}

我收到这样的错误:

vartypedict.cpp: In instantiation of ‘constexpr const int Tag2IDImpl<3, A, A>::value’:
vartypedict.cpp:39:109:   recursively required from ‘constexpr const int Tag2IDImpl<1, A, C, D, A>::value’
vartypedict.cpp:39:109:   required from ‘constexpr const int Tag2IDImpl<0, A, B, C, D, A>::value’
vartypedict.cpp:44:69:   required from ‘constexpr const int Tag2ID<A, B, C, D, A>::value’
vartypedict.cpp:97:36:   required from here
vartypedict.cpp:39:109: error: wrong number of template arguments (2, should be at least 3)
   39 |   constexpr static int value = std::is_same_v<TTag, Head>? counter : Tag2IDImpl<counter+1, TTag, Tails...>::value;
      |                                                                                                             ^~~~~
vartypedict.cpp:38:8: note: provided for ‘template<long unsigned int counter, class TTag, class Head, class ... Tails> struct Tag2IDImpl’
   38 | struct Tag2IDImpl {

如果我使用如下函数模板,它可以工作:

template <size_t counter, typename TTag, typename THead, typename...TTails>
constexpr size_t Tag2IDImpl() {
  if(std::is_same_v<TTag, THead>) {
    return counter;
  } else {
    if constexpr(sizeof...(TTails)>0) {
      return Tag2IDImpl<counter+1, TTag, TTails...>();
    } else {
      return counter+1;
    }
  }

}

template <typename TTag, typename... TParameters>
constexpr size_t Tag2ID() {
  return Tag2IDImpl<0, TTag, TParameters...>();
}

我的问题是为什么函数模板递归调用“Tag2IDImpl()”可以匹配到函数模板“template ",哪个使用struct,报错不匹配?

【问题讨论】:

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


【解决方案1】:

我的猜测是你没有一个好的递归结束评估。 我更习惯使用 constexpr 模板函数,使用 constexpr if,到现在为止,使用 structs 来进行递归计算。所以这是我的看法: (注意我从 1 开始计数,所以我得到 4 作为 id,对于 4 种不同的类型,如果这就是你想要做的)

#include <type_traits>
#include <iostream>

template <std::size_t counter, typename TTag, typename Head, typename... Tails>
constexpr static int Tag2IDImpl()
{
    if constexpr (sizeof...(Tails) > 0)
    {
        return  std::is_same_v<TTag, Head> ? counter : Tag2IDImpl<counter + 1, TTag, Tails...>();
    }
    else
    {
        // the recursion on the last step should be special and your code doesn't do that.
        return std::is_same_v<TTag, Head> ? counter : counter + 1;
    }
}


template <typename TTag, typename... TParameters>
constexpr static int Tag2ID()
{
    return Tag2IDImpl<1, TTag, TParameters...>();
}

struct A;
struct B;
struct C;
struct D;


int main()
{
    // outputs 4 distinct types
    std::cout << Tag2ID<A, B, C, D, A>() << std::endl;
}

【讨论】:

  • 是的,我有点明白这里发生了什么,我只是好奇如果我不想使用函数模板,如何解决这个问题?
  • 这个函数是在编译时评估的,如果 constexpr 是在 C++17 中引入的,让元模板编程感觉更像“正常”编程。为此,您需要为 counter == 0 添加一个特化。
【解决方案2】:

当您使用 if constexpr 时,如果 constexpr bool 有效,则 else 分支不会在编译时评估,而当您使用运算符 ? 时,您需要两个分支都有效。 为了避免 if,请尝试部分专门化退出条件,例如:

template <size_t N, typename TTag, typename Head, typename... Tails>
struct Tag2IDImpl {
    static constexpr size_t value = Tag2IDImpl<N+1,TTag,Tails...>::value;
};

template <size_t N, typename TTag>
struct Tag2IDImpl<N,TTag,TTag> {
    static constexpr size_t value = N;
};

template <typename TTag, typename... TParameters>
constexpr size_t Tag2ID() {
  return Tag2IDImpl<0,TTag, TParameters...>::value;
}

【讨论】:

  • 这很有意义。我想知道是否有任何技巧可以让它在不使用 if constexpr 的情况下工作?
  • 使用部分专业化以一种可能性编辑了答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-14
  • 1970-01-01
  • 2021-07-01
  • 2014-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多