C++如何实现可包装可调用对象的constexpr数组完成编译时下标校验
方案1:最小改动实现编译期越界检查
不需要修改原有代码的数组和std::function定义,仅在FunToCall模板中添加静态断言即可实现编译期越界校验:
// 原有代码不变,仅修改FunToCall实现 template <int i> FunT FunToCall() { static_assert(i >= 0 && i < a.size(), "index out of bound"); return a[i]; }
std::array::size是编译期常量,静态断言会在模板实例化阶段触发,只要传入的索引越界会直接编译报错,不会等到运行期抛出异常。
方案2:实现constexpr版本数组
std::function因为存在非平凡析构函数,不属于字面量类型,无法用于constexpr变量声明,要实现constexpr数组需要替换std::function为字面量类型的可调用对象:
核心调整点
- 将
FunT从std::function改为普通函数指针类型,函数指针属于字面量类型,符合constexpr要求 - 调整lambda实现为无捕获模式,将Foo实例作为调用参数传入,无捕获lambda可隐式转换为函数指针,支持constexpr上下文
完整可运行代码
#include <array> struct Foo { void m1(int) {} int* m2() { return &x; } int x = 0; }; // 改用函数指针类型,属于字面量类型 using FunT = bool(*)(Foo&, int); constexpr FunT Make1(void(Foo::*pf)(int)) { return [pf](Foo& foo, int arg) { (foo.*pf)(arg); return true; }; } constexpr FunT Make2(int*(Foo::*pf)()) { return [pf](Foo& foo, int arg) { [[maybe_unused]] auto res = (foo.*pf)(); return true; }; } // 现在可以正常声明为constexpr数组 static constexpr std::array a = { Make1(&Foo::m1), Make2(&Foo::m2) }; template <int i> constexpr FunT FunToCall() { static_assert(i >= 0 && i < a.size(), "FunToCall index out of range"); return a[i]; } template <int... is> bool make_calls(Foo& foo, int arg) { return (FunToCall<is>()(foo, is) && ...); } int main() { Foo foo; make_calls<0,1>(foo, 5); // 正常编译运行 // make_calls<0,1,2>(foo, 5); // 编译报错,触发静态断言 }
该方案要求编译器支持C++17及以上标准,无额外运行时开销,同时满足编译期越界检查和constexpr数组的要求。
内容的提问来源于stack exchange,提问作者giik
相关产品推荐
相关产品推荐

