static_assert 仅在编译时调用

FMa*_*iro 5 c++ constexpr c++17

给定一个 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"
}
Run Code Online (Sandbox Code Playgroud)

神螺栓:https://godbolt.org/z/nYora1b6M

Sto*_*ica 2

我会考虑的一种选择是这样写

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 "";
    }
}
Run Code Online (Sandbox Code Playgroud)

常量求值的一个控制原则是,当对表达式进行常量求值时,核心语言中不允许出现未定义的行为。从具有非 void 返回类型的函数末尾流出的就是编译器将捕获并报告的 UB 实例之一。
然后,用户将被引导至函数的末尾,希望断言消息能够为他们提供线索。在常规运行时评估中,我们得到通常的assert逻辑 - 在没有异常的情况下,如您所指定的。


归档时间:

查看次数:

206 次

最近记录:

2 年,12 月 前