static_assert 仅在编译时调用[重复]

问题描述 投票:0回答:1

给定一个 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

c++ c++17 constexpr
1个回答
1
投票

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

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
逻辑 - 在没有异常的情况下,如您指定的那样。

© www.soinside.com 2019 - 2024. All rights reserved.