C++中如何用结构体常量初始化enum class以表示音符时长?
问题:用enum class表示音符时长的定点数赋值问题
我想用enum class象征性表示音符时长,但枚举仅支持int类型,所以计划用定点数存储,后续通过除法转换为原double值。尝试用结构体存定点常量时,编译器报错“表达式必须是常量值”,因为结构体成员不是编译期常量。代码如下:
const double fp_multiplier = 10000.00; //utility const for fixed point conversion // fixed point representations of note values struct { const int whole = (int)(1.0*fp_multiplier); const int half = (int)(0.5*fp_multiplier); const int eigth = (int)(0.125*fp_multiplier); const int sixteenth = (int)(0.0625*fp_multiplier); const int dotted_eigth = (int)((eigth + 0.5*eigth)*fp_multiplier); const int dotted_sixteenth = (int)((sixteenth + 0.5*sixteenth)*fp_multiplier); } NoteValues; enum class NoteValue { whole = NoteValues.whole, //not allowed half = NoteValues.half, eigth = NoteValues.eigth, sixteenth = NoteValues.sixteenth, dotted_eigth = NoteValues.dotted_eigth, dotted_sixteenth = NoteValues.dotted_sixteenth, };
我知道这是因为NoteValues.whole不是编译期常量(和const关键字无关),想知道怎么用结构体常量给enum class赋值,或者有没有更优的替代方案,必须使用enum class。
可行方案
方案1:用constexpr定义编译期常量(推荐)
把所有需要的常量都用constexpr修饰,确保它们在编译期就能确定值,这样就能直接用于初始化enum class。优先用整数运算处理附点音符,避免浮点精度问题:
constexpr double fp_multiplier = 10000.00; constexpr int whole_note = static_cast<int>(1.0 * fp_multiplier); constexpr int half_note = static_cast<int>(0.5 * fp_multiplier); constexpr int eigth_note = static_cast<int>(0.125 * fp_multiplier); constexpr int sixteenth_note = static_cast<int>(0.0625 * fp_multiplier); constexpr int dotted_eigth_note = eigth_note * 3 / 2; // 整数运算更可靠 constexpr int dotted_sixteenth_note = sixteenth_note * 3 / 2; enum class NoteValue { whole = whole_note, half = half_note, eigth = eigth_note, sixteenth = sixteenth_note, dotted_eigth = dotted_eigth_note, dotted_sixteenth = dotted_sixteenth_note, };
方案2:用constexpr结构体存储常量
如果想把常量归类到结构体里,需要把结构体成员和实例都声明为constexpr,确保编译期初始化:
constexpr double fp_multiplier = 10000.00; constexpr struct NoteValues { constexpr int whole = static_cast<int>(1.0 * fp_multiplier); constexpr int half = static_cast<int>(0.5 * fp_multiplier); constexpr int eigth = static_cast<int>(0.125 * fp_multiplier); constexpr int sixteenth = static_cast<int>(0.0625 * fp_multiplier); constexpr int dotted_eigth = eigth * 3 / 2; constexpr int dotted_sixteenth = sixteenth * 3 / 2; } note_values; enum class NoteValue { whole = note_values.whole, half = note_values.half, eigth = note_values.eigth, sixteenth = note_values.sixteenth, dotted_eigth = note_values.dotted_eigth, dotted_sixteenth = note_values.dotted_sixteenth, };
方案3:直接使用计算后的字面量(可读性差,不推荐)
如果不想用constexpr,可以直接把每个定点数的计算结果作为字面量写进enum class,但后续修改乘数时需要重新计算所有值,维护成本高:
enum class NoteValue { whole = 10000, half = 5000, eigth = 1250, sixteenth = 625, dotted_eigth = 1875, dotted_sixteenth = 937 };
补充说明
enum class的枚举值必须是编译期常量表达式,普通const变量如果依赖运行时计算,无法用于初始化枚举值。- 用整数运算替代浮点运算处理附点音符,既能保证编译期求值,又能避免浮点精度丢失的问题。
内容的提问来源于stack exchange,提问作者Blargian
相关产品推荐
相关产品推荐

