给定一个 constexpr 函数,如果在编译时调用该函数,是否有办法创建一个编译时错误,如果在运行时调用该函数,是否有返回标记值的方法?
不幸的是,我不能使用异常,因为它们在构建中被禁用了。
这将主要用于在枚举和字符串之间进行相互转换。如果开发人员输入了不正确的值,与其希望他们在运行时看到错误,不如让构建失败更好,但是由于我们可以从未知来源获取值,因此该值有可能无效,我们不不想在运行时崩溃。
演示用例:
#include <fmt/core.h>
#include <iostream>
// from: https://stackoverflow.com/a/63529662/4461980
// if C++20, we will need a <type_traits> include for std::is_constant_evaluated
#if __cplusplus >= 202002L
#include <type_traits>
#endif
constexpr bool is_constant_evaluated() {
#if __cplusplus >= 202002L
return std::is_constant_evaluated();
#elif defined(__GNUC__) // defined for both GCC and clang
return __builtin_is_constant_evaluated();
#else
// If the builtin is not available, return a pessimistic result.
// This way callers will implement everything in a constexpr way.
return true;
#endif
}
enum class MyEnum { A, B, C, END_OF_ENUM };
constexpr const char* ToString(MyEnum value) {
switch (value) {
case MyEnum::A: {
return "A";
}
case MyEnum::B: {
return "B";
}
case MyEnum::C: {
return "C";
}
case MyEnum::END_OF_ENUM:
default: {
if (is_constant_evaluated()) {
// compile time error?
return "test";
} else {
return "UNKNOWN";
}
}
}
// unreachable?
return "";
}
int main(int argc, char** argv) {
// strcitly-speaking not UB since 5 is 0b101 in binary which does not use more bits than the
// highest item which would have value 4 which would be 0b100
// for demo purposes only, don't scream at me
constexpr auto stringified = ToString(static_cast<MyEnum>(5));
fmt::print("{}\n", stringified); // prints "test"
fmt::print("{}\n", ToString(static_cast<MyEnum>(5))); // prints "UNKNOWN"
}
godbolt:https://godbolt.org/z/nYora1b6M
我会考虑的一个选择是这样写
constexpr const char* ToString(MyEnum value) {
switch (value) {
case MyEnum::A: {
return "A";
}
case MyEnum::B: {
return "B";
}
case MyEnum::C: {
return "C";
}
case MyEnum::END_OF_ENUM:
default: ;
}
if(!is_constant_evaluated()) {
assert(0 && "Converting value out of range"!);
return "";
}
}
常量求值的管理原则是,当表达式常量求值时,核心语言中不允许有任何未定义的行为。从具有非 void 返回类型的函数末尾流出就是这样一个 UB 实例,编译器将捕获并报告它。
然后用户被引导到函数的末尾,希望 assert 消息能给他们提供线索。在常规运行时评估中,我们得到通常的
assert
逻辑 - 在没有异常的情况下,如您指定的那样。