To force checks at compile time the construction from the chrono type needs to be consteval. There is no way around it.
consteval explicit delay_t(duration<int64_t, std::nano> delay) : delay_t {wrapped_{delay}} {}; To allow also runtime construction we needs an alternative way to construct delay_t. We can for example use a static method runtime that is constexprstatic method that is constexpr.
constexpr static auto runtime(duration<int64_t, std::nano> delay) -> delay_t { return delay_t {wrapped_ {delay}}; } As this method needs to construct delay_t in some different way, we need a second non-consteval constructor. But as we already have one for chrono, we need to introduce a private wrapper type:
private: struct wrapped_ { duration<int64_t, std::nano> value; }; constexpr delay_t(wrapped_ delay) : value {delay.value} { if (value != delay.value) { throw std::runtime_error("delay cannot be represented."); } }; Using delay_t now enforces compile-time checks by default:
foo(delay_t {1s}); foo(delay_t {10s}); // gives compile time error foo(delay_t::runtime(10s)); // gives a runtime error