You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现编译时获取C++ integer_sequence元素索引的元函数?

实现编译时获取枚举类型integer_sequence元素索引的通用元函数

刚好我之前也折腾过类似的需求,完全不用引入那些大体积的库,咱们用C++的可变参数模板和递归匹配就能实现这个通用的编译时索引查询元函数,来看看具体怎么做吧!

核心思路

我们要做的是一个编译时元函数,它会递归遍历integer_sequence的元素:

  • 当序列的第一个元素就是目标值时,直接返回当前的索引(从0开始计数)
  • 如果不是目标,就递归处理剩余的序列,同时把索引加1
  • 如果遍历完整个序列都没找到目标,可以选择触发编译错误,或者返回一个标记值(比如std::size_t(-1)

完整代码实现

#include <iostream>
#include <utility>
#include <type_traits>

namespace animalsExperiment{
enum class Animals { CAT = 15, DOG = 19, RABBIT = 43 };

constexpr auto AllAnimals = std::integer_sequence<Animals, Animals::CAT, Animals::DOG, Animals::RABBIT>{};

// 基础模板:作为递归的入口
template<typename Seq, Animals Target, std::size_t CurrentIndex = 0>
struct IndexOf;

// 特化1:匹配到目标元素的情况,直接返回当前索引
template<Animals Target, std::size_t CurrentIndex, Animals... Rest>
struct IndexOf<std::integer_sequence<Animals, Target, Rest...>, Target, CurrentIndex> {
    static constexpr std::size_t value = CurrentIndex;
};

// 特化2:未匹配到目标,递归处理剩余序列,索引+1
template<Animals First, Animals Target, std::size_t CurrentIndex, Animals... Rest>
struct IndexOf<std::integer_sequence<Animals, First, Rest...>, Target, CurrentIndex> {
    static constexpr std::size_t value = IndexOf<std::integer_sequence<Animals, Rest...>, Target, CurrentIndex + 1>::value;
};

// 特化3:序列遍历完毕仍未找到目标,触发编译错误
template<Animals Target, std::size_t CurrentIndex>
struct IndexOf<std::integer_sequence<Animals>, Target, CurrentIndex> {
    static_assert(!std::is_same_v<Animals, Animals>, "Target element not found in the integer_sequence");
    static constexpr std::size_t value = -1; // 实际不会执行到这里,静态断言会先报错
};

// 辅助变量模板:简化调用,不用每次写::value
template<typename Seq, Animals Target>
constexpr std::size_t index_of = IndexOf<Seq, Target>::value;
}

// 测试代码
int main() {
    using namespace animalsExperiment;
    // 编译时验证正确性
    static_assert(index_of<decltype(AllAnimals), Animals::CAT> == 0);
    static_assert(index_of<decltype(AllAnimals), Animals::DOG> == 1);
    static_assert(index_of<decltype(AllAnimals), Animals::RABBIT> == 2);
    // 取消注释下面这行会触发编译错误:提示目标不在序列中
    // static_assert(index_of<decltype(AllAnimals), Animals::PIG> == 3);

    // 运行时输出验证
    std::cout << "CAT index: " << index_of<decltype(AllAnimals), Animals::CAT> << std::endl;
    std::cout << "DOG index: " << index_of<decltype(AllAnimals), Animals::DOG> << std::endl;
    std::cout << "RABBIT index: " << index_of<decltype(AllAnimals), Animals::RABBIT> << std::endl;
    return 0;
}

代码细节解释

  1. 基础模板IndexOf:负责启动递归,参数包括序列类型、目标枚举值,还有可选的当前索引(默认从0开始)。
  2. 第一个特化版本:当序列的第一个元素就是我们要找的目标时,直接返回当前的索引值,递归终止。
  3. 第二个特化版本:如果第一个元素不是目标,就把序列的剩余部分传给下一层递归,同时索引加1,继续查找。
  4. 第三个特化版本:当序列被遍历完(变成空序列)还没找到目标,用static_assert触发编译错误,给开发者明确的提示。
  5. 辅助变量模板index_of:简化调用语法,不用每次写IndexOf<...>::value,直接用index_of<序列类型, 目标值>就能拿到结果。

可选扩展:返回标记值替代编译错误

如果你希望目标不存在时返回一个特定值(比如std::size_t(-1))而不是直接报错,可以修改第三个特化版本:

template<Animals Target, std::size_t CurrentIndex>
struct IndexOf<std::integer_sequence<Animals>, Target, CurrentIndex> {
    static constexpr std::size_t value = std::size_t(-1);
};

这样即使目标不在序列中,也能在编译时得到一个可识别的标记值,方便后续处理。

内容的提问来源于stack exchange,提问作者Marcello Mura

火山引擎 最新活动