C++20中如何让constexpr构造器对常量参数执行编译期检查
解决方案:C++20中为chrono强类型实现编译期溢出检查并兼容运行时输入
针对你遇到的问题——既要对delay_t{10s}这类常量字面量做编译期溢出检查,又要支持运行时传入的值——可以借助C++20的std::is_constant_evaluated()和constexpr构造函数实现两全的方案,核心思路是区分编译期与运行时求值场景,分别触发编译错误和运行时异常。
修改后的完整代码
#include <chrono> #include <cstdint> #include <stdexcept> #include <type_traits> // 自定义支持constexpr构造的异常类(C++20标准库异常暂不支持constexpr构造) struct constexpr_delay_error : std::exception { const char* what() const noexcept override { return "delay cannot be represented within int32_t nanoseconds"; } }; struct delay_t { std::chrono::duration<int32_t, std::nano> value {}; constexpr explicit delay_t(std::chrono::duration<int64_t, std::nano> delay) : value(delay) { using target_type = std::chrono::duration<int32_t, std::nano>; const auto delay_count = delay.count(); // 检查是否超出目标类型的取值范围 const bool out_of_range = delay_count < target_type::min().count() || delay_count > target_type::max().count(); if (out_of_range) { if (std::is_constant_evaluated()) { // 编译期求值时抛出constexpr异常,触发编译错误 throw constexpr_delay_error{}; } else { // 运行时求值时抛出常规异常 throw std::runtime_error("delay cannot be represented within int32_t nanoseconds"); } } }; }; auto foo(delay_t delay) -> void {} auto main() -> int { using namespace std::chrono_literals; foo(delay_t {10s}); // 现在会触发编译期错误 // 运行时场景示例: // int64_t user_input = ...; // foo(delay_t {std::chrono::nanoseconds{user_input}}); // 运行时超出范围则抛异常 return 0; }
关键细节说明
std::is_constant_evaluated():C++20引入的函数,用于判断当前代码是否处于常量表达式求值阶段(比如用字面量构造delay_t时)。- constexpr异常类:由于C++20中标准库的
std::runtime_error不支持constexpr构造,因此自定义一个可在constexpr上下文中抛出的异常类,编译期抛出该异常会直接触发编译错误。 - 范围检查逻辑:通过对比输入duration的计数值与目标类型(
int32_t纳秒)的最小/最大值,判断是否存在溢出,比直接对比value != delay更直观准确。
替代方案:使用static_assert
如果偏好更直接的编译期断言,可以调整构造函数的检查逻辑,利用if constexpr确保仅在常量求值阶段触发static_assert:
constexpr explicit delay_t(std::chrono::duration<int64_t, std::nano> delay) : value(delay) { using target_type = std::chrono::duration<int32_t, std::nano>; const auto delay_count = delay.count(); if constexpr (std::is_constant_evaluated()) { static_assert( delay_count >= target_type::min().count() && delay_count <= target_type::max().count(), "delay cannot be represented within int32_t nanoseconds" ); } else { if (delay_count < target_type::min().count() || delay_count > target_type::max().count()) { throw std::runtime_error("delay cannot be represented within int32_t nanoseconds"); } } }
这种方式会在编译期直接输出自定义的断言错误信息,可读性更强。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

