[英]How to check for overflow in duration_cast
我需要將一種std::chrono::duration
為另一種類型,但我需要知道何時無法進行此類轉換,因為該值無法表示。
我還沒有在標准庫中找到任何工具來檢查這一點。 cppreference 頁面沒有指定如果值超出范圍會發生什么,只有從浮點到整數的轉換可能是未定義的行為(在我的情況下,我需要從整數轉換為整數)。
沒有一刀切的解決方案,但是適合許多用例的解決方案是使用基於double
精度的duration
進行范圍檢查。 也許是這樣的:
#include <chrono>
#include <iostream>
#include <stdexcept>
template <class Duration, class Rep, class Period>
Duration
checked_convert(std::chrono::duration<Rep, Period> d)
{
using namespace std::chrono;
using S = duration<double, typename Duration::period>;
constexpr S m = Duration::min();
constexpr S M = Duration::max();
S s = d;
if (s < m || s > M)
throw std::overflow_error("checked_convert");
return duration_cast<Duration>(s);
}
int
main()
{
using namespace std::chrono;
std::cout << checked_convert<nanoseconds>(10'000h).count() << "ns\n";
std::cout << checked_convert<nanoseconds>(10'000'000h).count() << "ns\n";
}
對我來說,這個輸出:
36000000000000000ns
libc++abi.dylib: terminating with uncaught exception of type std::overflow_error: checked_convert
在將constexpr
添加到 Howards 答案后,我注意到以下
static_assert(checked_convert<nanoseconds>(nanoseconds::max()) == nanoseconds::max());
導致編譯錯誤
<source>: In function 'int main()':
<source>:23:68: error: non-constant condition for static assertion
23 | static_assert(checked_convert<nanoseconds>(nanoseconds::max()) == nanoseconds::max());
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
<source>:23:66: in 'constexpr' expansion of 'checked_convert<std::chrono::duration<long int, std::ratio<1, 1000000000> >, long int, std::ratio<1, 1000000000> >(std::chrono::duration<long int, std::ratio<1, 1000000000> >::max())'
<source>:16:35: in 'constexpr' expansion of 'std::chrono::duration_cast<std::chrono::duration<long int, std::ratio<1, 1000000000> >, double, std::ratio<1, 1000000000> >(s)'
/opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/chrono:200:21: in 'constexpr' expansion of 'std::chrono::__duration_cast_impl<std::chrono::duration<long int, std::ratio<1, 1000000000> >, std::ratio<1>, double, true, true>::__cast<double, std::ratio<1, 1000000000> >((* & __d))'
<source>:23:68: error: overflow in constant expression [-fpermissive]
基於霍華德的回答,我建議以下不受此問題影響的內容。
#include <chrono>
#include <iostream>
#include <stdexcept>
#include <type_traits>
template<class Duration, class Rep, class Period>
constexpr auto checked_convert(std::chrono::duration<Rep, Period> d)
-> std::enable_if_t<!std::is_same_v<Duration, decltype(d)>, Duration> {
using namespace std::chrono;
using S = duration<double, typename Duration::period>;
constexpr S m = Duration::min();
constexpr S M = Duration::max();
S s = d;
if(s < m || s > M) { throw std::overflow_error("checked_convert"); }
return duration_cast<Duration>(s);
}
template<class Duration>
constexpr Duration checked_convert(Duration d) {
return d;
}
int main() {
using namespace std::chrono;
static_assert(checked_convert<nanoseconds>(nanoseconds::max()) == nanoseconds::max());
std::cout << checked_convert<nanoseconds>(nanoseconds::max()).count() << "ns\n";
std::cout << checked_convert<nanoseconds>(10'000h).count() << "ns\n";
std::cout << checked_convert<nanoseconds>(10'000'000h).count() << "ns\n";
}
第二個重載確保當 from 和 to Duration 的類型相同時不會發生轉換。
我擺脫 UB 的另一種方法是將 Howars 解決方案更改為以下內容:
#include <chrono>
#include <iostream>
#include <stdexcept>
template <class Duration, class Rep, class Period>
constexpr Duration
checked_convert(std::chrono::duration<Rep, Period> d)
{
using namespace std::chrono;
using S = duration<double, typename Duration::period>;
constexpr S m = Duration::min();
constexpr S M = Duration::max();
S s = d;
if (s < m || s > M)
throw std::overflow_error("checked_convert");
return duration_cast<Duration>(d);
}
int
main()
{
using namespace std::chrono;
static_assert(checked_convert<nanoseconds>(nanoseconds::max()) == nanoseconds::max());
std::cout << checked_convert<nanoseconds>(10'000h).count() << "ns\n";
std::cout << checked_convert<nanoseconds>(10'000'000h).count() << "ns\n";
}
注意從return duration_cast<Duration>(s)
到return duration_cast<Duration>(d)
。 這讓 chrono 在兩個 Duration 相同時處理問題,但我不確定帶有 d 的 duration_cast 是否對其他情況有效。
請注意,我尚未在許多情況下測試過這兩種解決方案中的任何一種。 很可能隱藏了其他可能發生溢出的情況。 我對浮點運算不夠精通,無法驗證答案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.