constexpr函数无法在constexpr if中使用的解决方案咨询
问题背景
可以在constexpr函数中通过条件分支调用非constexpr函数,直接调用这类函数时编译运行都正常,示例如下:
void bla( ) { std::cout << "bla called!" << std::endl; } constexpr bool check(bool condition) { //bla(); // 无法直接调用 -> 非constexpr! condition ? void (0) : bla(); // 编译运行正常,无论condition为true或false! true ? void(0): bla(); // 同样可编译!已直接求值 false ? bla(): void(0); // 可编译,已直接求值 return 0; } int main() { check( false ); check( true ); }
比如check(false)能正常调用非constexpr的bla()函数。
constexpr if调用时的编译错误
但将这类constexpr函数用于constexpr if时,会触发编译错误:
void bla( ) { std::cout << "bla called!" << std::endl; } constexpr bool check(bool condition) { //bla(); // 无法直接调用 -> 非constexpr! condition ? void (0) : bla(); // 编译运行正常,无论condition为true或false! true ? void(0): bla(); // 同样可编译!已直接求值 false ? bla(): void(0); // 可编译,已直接求值 return 0; } int main() { if constexpr(check( false )){}; // 抛出编译错误 if constexpr(check( true )){}; }
原因是constexpr if要求条件必须是核心常量表达式,编译器会检查函数所有可能的执行路径是否符合constexpr要求,哪怕某些路径在运行时不会被执行。
通用场景与现有方案痛点
实际需求是根据编译标志实现函数的两个版本,通过constexpr if调用保持代码简洁,但定义SIMULATION时无法正常运行:
#include <iostream> #define SIMULATION size_t get_count() { return 10; } #if defined(SIMULATION) inline constexpr bool func(bool compile_time_flag = false) // 添加compile_time_flag参数,使函数在flag=true时可constexpr求值 { bool ret_val = false; ret_val = compile_time_flag ? false : (get_count() > 1); return ret_val; } #else inline constexpr bool func(bool compile_time_flag = true) { return false; }; #endif int main() { if constexpr(func()) { std::cout << " It works! "; } return 0; }
不想通过编译标志区分调用方式(如下述不够优雅的实现),需要更简洁的方案:
#if define SIMULATION # define CHECK_COND (func()) #else # define CHECK_COND constexpr(func()) int main() { if CHECK_COND { std::cout << " It works! "; } return 0; }
优雅解决方案
方案1:利用C++20 std::is_constant_evaluated()(最简洁)
std::is_constant_evaluated()能在constexpr函数内部判断当前是否处于编译期求值,从而分支处理两种逻辑:
#include <iostream> #include <type_traits> #define SIMULATION size_t get_count() { return 10; } inline constexpr bool func() { #if defined(SIMULATION) if(std::is_constant_evaluated()) { // 编译期求值路径,仅返回合法constexpr值 return false; } else { // 运行时路径,允许调用非constexpr函数 return (get_count() > 1); } #else return false; #endif } int main() { // constexpr if触发编译期求值,走合法路径 if constexpr(func()) { std::cout << " It works! "; } // 运行时调用走非constexpr路径 bool runtime_result = func(); return 0; }
方案2:模板非类型参数区分编译/运行路径
通过模板参数明确标记是否为编译期调用,编译器会实例化对应合法路径:
#include <iostream> #define SIMULATION size_t get_count() { return 10; } template<bool CompileTimeFlag = false> inline constexpr bool func() { #if defined(SIMULATION) if constexpr(CompileTimeFlag) { // 编译期专用分支,无非法操作 return false; } else { // 运行时分支,允许调用非constexpr函数 return (get_count() > 1); } #else return false; #endif } int main() { // constexpr if调用时显式指定编译期分支 if constexpr(func<true>()) { std::cout << " It works! "; } // 运行时调用使用默认参数 bool runtime_result = func(); return 0; }
方案3:consteval函数分离编译期逻辑(C++20+)
用consteval定义强制编译期求值的函数,原函数根据调用场景适配:
#include <iostream> #define SIMULATION size_t get_count() { return 10; } // 仅能在编译期求值的函数 consteval bool compile_time_func() { #if defined(SIMULATION) return false; #else return false; #endif } // 通用函数,自动适配编译/运行场景 constexpr bool func(bool compile_time_flag = false) { #if defined(SIMULATION) if constexpr(compile_time_flag) { return compile_time_func(); } else { return (get_count() > 1); } #else return false; #endif } int main() { // constexpr if调用时指定编译期标记 if constexpr(func(true)) { std::cout << " It works! "; } // 运行时调用使用默认参数 bool runtime_result = func(); return 0; }
内容的提问来源于stack exchange,提问作者Akash
相关产品推荐
相关产品推荐

