C++20模板参数循环

tzxcd3kk  于 2022-12-20  发布在  其他
关注(0)|答案(2)|浏览(162)

我有一组类,它们通常有一个成员函数,该函数将枚举作为模板参数。我想在这些类上循环。我正在尝试构建一个解决方案,该解决方案将自动在它们上循环。

struct fruit
{
  enum Value : uint8_t
  {
    apple,
    banana,
    kiwi,
  };

  fruit() = default;
  fruit(Value afruit) : value(afruit) { }
  Value value;
};

struct field
{
    template<fruit f> 
    void plant()
    {
    }
};

struct barn
{
    template<fruit f> 
    void store(int somearg)
    {
    }
};

struct fruit_loop
{
//?
};

int main(int argc, char** argv)
{

    auto f = field{};

    // replace with something like a for loop ?
    f.plant<fruit::apple>();
    f.plant<fruit::banana>();
    f.plant<fruit::kiwi>();

    return 0;
}
but5z9lq

but5z9lq1#

这在当前的标准C中是不可能的。没有办法决定枚举类型的哪些值具有命名枚举数。(尽管有一些基于编译器扩展或特定编译器行为的技巧来获得某种形式的反射。)
这将需要一个可以解析和修改C
源代码的工具进行外部预处理。
你唯一能做的就是在std::underlying_typestd::numeric_limits的帮助下,循环遍历底层类型的所有值,假设它是固定的。然而,这也会包括那些带有命名枚举数的枚举值之外的(有效的)枚举值。而且,在编译时这样做比简单的循环要复杂一些,并且对于大于uint8_t的底层类型是不可行的。
您可以编写如下函数

template<auto V>
constexpr inline auto constant = std::integral_constant<decltype(V), V>{};

constexpr void for_all_fruit(auto&& f) {
    f(constant<fruit::apple>);
    f(constant<fruit::banana>);
    f(constant<fruit::kiwi>);
}

然后在需要对每个值应用某个值时调用该函数:

auto f = field{};

for_all_fruit([&](auto v){ f.plant<v()>(); });

v()可以替换为v.value。或者也可以只替换为v,只要plant不使用占位符作为非类型模板参数的类型。)

qvsjd97n

qvsjd97n2#

正如其他人所说的,C++没有反射,也没有“通用的,适用于所有情况”的方法来做到这一点。
如果你愿意接受一些限制,并且能够遵循惯例,你可以这样做:

struct fruit
{
  enum Value : uint8_t
  {
    apple,  //All elements
    banana, //must be 
    kiwi,   //continuous, with no gaps

    count //MUST BE LAST element in enum
  };
//Rest of your code...
}

template<fruit::Value v>
void plantAll(field f)
{
    f.plant<v>();
    plantAll<(fruit::Value)(v+1)>(f);
}

template<>
void plantAll<fruit::Value::count>(field f)
{
    
}

int main()
{
    auto f = field{};
    plantAll<fruit::apple>(f);
}

这是如何工作的?我们创建一个模板函数,它递归地调用自己。我们还在枚举的末尾添加了一个'count'元素。
在模板化函数中,您使用给定的Value执行操作。然后递归调用该函数,但模板化参数是递增的枚举值(作为int),并强制转换回枚举类型。您为“count”类型创建一个特殊化,该特殊化不执行任何操作,从而结束递归。
你必须保持枚举中的所有值都是连续的--否则你会得到没有命名枚举数的值(你不希望这样)。你还必须保持COUNT在最后--否则你会错过一些值。

相关问题