C++:从非类型参数包初始化static constexpr成员遇阻求助
问题修复:编译期求值结构体static constexpr成员
核心问题分析
代码无法运行的原因有三个:
- 模板参数推导失败:
var<T>是模板类,作为非类型模板参数时,编译器无法从{"a", &s1::a}自动推导模板参数T,必须显式指定。 - 非类型模板参数不符合要求:原
var<T>没有constexpr构造函数,无法在编译期完成初始化,不满足C++对类类型非类型模板参数的字面类型要求。 - 成员指针转int是未定义行为:强制将成员指针
int T::*转为int属于UB,应该用标准的offsetof获取编译期成员偏移量。
修复方案与代码实现
步骤1:修复var<T>结构体
添加constexpr构造函数,使其成为字面类型,支持编译期初始化:
#include <cstddef> // 引入offsetof struct s1 { int a; int b; }; template <class T> struct var { const char* name; int T::*mem_ptr; // constexpr构造函数,支持编译期初始化 constexpr var(const char* n, int T::*ptr) : name(n), mem_ptr(ptr) {} };
步骤2:添加辅助推导函数(可选)
避免手动指定模板参数T,简化调用:
template <class T> constexpr auto make_var(const char* name, int T::*mem_ptr) { return var<T>{name, mem_ptr}; }
步骤3:修复A结构体的编译期求值
用offsetof替代非法的成员指针转int,确保编译期合法:
constexpr int sum(auto&&... n) { return (n + ...); } template <auto... Vars> struct A { static constexpr auto value = sum(offsetof(typename decltype(Vars)::value_type, *Vars.mem_ptr)...); }; // 原B结构体保持不变 template <int... Ts> struct B { static constexpr auto value = sum(Ts...); };
调用示例
// 两种合法调用方式 auto Help1 = A<var<s1>{"a", &s1::a}, var<s1>{"b", &s1::b}>::value; // 结果为0+4=4 auto Help2 = A<make_var("a", &s1::a), make_var("b", &s1::b)>::value; // 同上 auto Works = B<1,2,3>::value; // 结果为6,正常运行
最终目标:实现C结构体的参数展开
针对你需要的unknown函数参数展开形式(name, offset, name, offset...),实现如下:
// 第三方constexpr函数示例(模拟) constexpr auto unknown(auto&&... args) { // 示例逻辑:累加所有偏移量(忽略name参数) int total = 0; bool is_offset = false; ((is_offset ? total += args : void(), is_offset = !is_offset), ...); return total; } template <auto... Vars> struct C { // 展开为unknown("a", 0, "b", 4, ...)的形式 static constexpr auto value = unknown(Vars.name, offsetof(typename decltype(Vars)::value_type, *Vars.mem_ptr)...); };
调用C结构体
auto C_val = C<make_var("a", &s1::a), make_var("b", &s1::b)>::value; // 结果为0+4=4
注意事项
- 需使用C17或更高版本,因为C17才支持类类型作为非类型模板参数。
offsetof仅适用于标准布局类型,你的s1结构体符合要求。- 禁止将成员指针直接转换为整数,这会导致未定义行为,必须使用
offsetof获取编译期偏移量。
内容的提问来源于stack exchange,提问作者Maor Dahan
相关产品推荐
相关产品推荐

