21

I found an interesting gotcha with std::numeric_limits<seconds>::max() returning 0. The answer is to use seconds::max() or std::numeric_limits<seconds::rep>::max() instead, but I am interested to know why this happens. I would expect it to either fail at compile time or just work. The following code demonstrates the issue with gcc 4.9.3.

#include <iostream>
#include <limits>
#include <chrono> 

using namespace std;
using namespace std::chrono;

int main(int /*argc*/, const char* /*argv*/[])
{
    const auto maxSeconds = std::numeric_limits<seconds>::max();
    std::cerr << maxSeconds.count() << "\n";
    const auto maxSeconds2 = seconds::max();
    std::cerr << maxSeconds2.count() << "\n";
   return 0;
}

I can't see any implicit conversions in the chrono header file. If a duration had implicitly cast to a numeric type and the sign was lost or a bool you could end up with a minimum of zero - but a maximum of zero doesn't make sense.


As TartanLlama points out the default specialization uses the default constructor and therefore returns 0.

Delving into an old copy of the standard I see the following dictats:

18.3.2.3 Class template numeric_limits [numeric.limits]

Non-arithmetic standard types, such as complex<T> (26.4.2), shall not have specializations.

and a little later:

The default numeric_limits<T> template shall have all members, but with 0 or false values.

The value of each member of a specialization of numeric_limits on a cv-qualified type cv T shall be equal to the value of the corresponding member of the specialization on the unqualified type T.

What is missing is an explanation of why this was considered a better idea by the committee than a compilation failure. Is a library defect report warranted?


Update: I have raised this as an issue with the ISO committee

https://issues.isocpp.org/show_bug.cgi?id=186

5
  • 1
    Possible duplicate of Why is C++ numeric_limits<enum_type>::max() == 0?
    – awesoon
    Commented Feb 23, 2016 at 11:30
  • This is a duplicate. I missed that. However, the best answer to that question is the same as this one's so far "Because the standard says so". I'm looking for a little more insight. Why does the standard say so? Commented Feb 23, 2016 at 11:44
  • 1
    I'm also looking for a reason of such design, but, unfortunately, without effect. Your question is not a duplicate anymore and I've retracted my close vote.
    – awesoon
    Commented Feb 23, 2016 at 11:59
  • I think this is another archaic underexploitation of the language features. (like stackoverflow.com/questions/21936507/…) perhaps what you are supposed to do replace std::numeric_limits<T>::max() by `std::numeric_limit<T>::is_specialized::value?std::numeric_limits<T>::max():throw 0;' (or some clever compile time equivalent.)
    – alfC
    Commented Mar 16, 2016 at 20:35
  • By the way, std::numeric_limits<T>::max() should be by default T::max() and if that doesn't exist then it should be a compiler error. (And don't get us started with std::numeric_limits<T>::min() which is an inconsistent mess for floating and integer types.)
    – alfC
    Commented Mar 16, 2016 at 20:48

2 Answers 2

12

std::chrono::seconds itself is not a standard arithmetic type, thus std::numeric_limits is not specialized for it. So you just see some rather useless defaults.

To query the range of the underlying type used to count the ticks (which, under gcc, is the 64 bit long int), use

std::numeric_limits<seconds::rep>::max();

instead.

11

std::numeric_limits is not specialized for std::chrono::seconds. Default definitions are given for all data members and functions in std::numeric_limits to avoid compiler errors for unspecialized types. The default version of numeric_limits<T>::max() simply returns T(), which is 0 in this case.

You can check if std::numeric_limits is specialized for a given T at compile time by checking std::numeric_limits<T>::is_specialized, which defaults to false.

5
  • 2
    I see it in the limits header now. But why does the default version of numeric_limits<T>::max() use the default constructor. Surely it would be better to have a compiler error here? Commented Feb 23, 2016 at 11:23
  • 1
    @BruceAdams Honestly I'm not sure. I would have thought leaving it out and letting users use SFINAE instead would be better, but maybe I'm missing something. Commented Feb 23, 2016 at 11:38
  • You and me both. I'll leave the question open for a language lawyer and consider a library defect report if none turn up after sufficient wait. Commented Feb 23, 2016 at 11:45
  • 3
    @Bruce - The <limits> header goes waaay back, to times when SFINAE wasn't as popular as it is today. And without if constexpr, it is often an advantage if both paths will compile.
    – Bo Persson
    Commented Mar 16, 2016 at 17:26
  • @Bo That sounds plausible. Perhaps you can demonstrate a case where that would be have be useful? I'm finding it hard to picture. Commented Mar 16, 2016 at 22:27

Not the answer you're looking for? Browse other questions tagged or ask your own question.