C++双参数函数仅第二个为编译期常量时的constexpr优化方案
正确实现方案
原代码的核心问题:普通函数参数mask不属于编译期常量表达式,哪怕调用时传入字面量,也不能直接用于if constexpr的条件判断,会直接编译失败。
惯用写法(C++17及以上)
将第二个编译期常量参数改为模板非类型参数,这是最符合C++规范的实现方式,模板天生自带隐式inline属性,无需额外声明即可保证头文件多编译单元引入不冲突,同时编译器可以100%在编译期完成if constexpr的分支判定,完全消除运行时分支,且可以轻松内联整个函数。
代码实现:
template <uint8_t mask> // 编译期常量作为模板参数 uint8_t convert_bcd(uint8_t num) { // inline可省略,模板默认隐式inline uint8_t result = mask & 0x0F & num; if constexpr ((mask & 0xF0) != 0) result += 10 * ((mask & 0xF0 & num) >> 4); return result; }
调用方式:
uint8_t result1 = convert_bcd<0x7F>(data[0]); uint8_t result2 = convert_bcd<0x3F>(data[1]);
关键字用法说明(解决混淆)
inline:当前C++标准中,该关键字的核心作用是允许函数在多个编译单元重复定义(即可以写在头文件中),而非强制编译器内联函数,是否内联由编译器的优化策略自行判断。模板函数默认自带隐式inline属性,因此上述代码中inline关键字可以省略。const:仅表示运行时变量的值不可修改,和编译期常量没有关系,不能用于要求编译期常量的场景。constexpr:表示对应的表达式/函数/变量可以在编译期求值,if constexpr的条件必须是编译期常量表达式,只有模板参数、constexpr修饰的变量/常量表达式可以作为其判断条件。
低版本兼容方案(C11/C14)
如果使用的标准不支持if constexpr,可以用模板特化实现相同效果:
template <uint8_t mask, bool has_high_bit = ((mask & 0xF0) != 0)> struct BcdConverter; template <uint8_t mask> struct BcdConverter<mask, false> { static uint8_t convert(uint8_t num) { return mask & 0x0F & num; } }; template <uint8_t mask> struct BcdConverter<mask, true> { static uint8_t convert(uint8_t num) { uint8_t result = mask & 0x0F & num; result += 10 * ((mask & 0xF0 & num) >> 4); return result; } }; template <uint8_t mask> uint8_t convert_bcd(uint8_t num) { return BcdConverter<mask>::convert(num); }
调用方式和C++17版本完全一致,同样可以实现编译期分支消除和内联。
内容的提问来源于stack exchange,提问作者Sergey Kostrukov
相关产品推荐
相关产品推荐

