20

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

Bruce Adams
  • 4,953
  • 4
  • 48
  • 111
  • 1
    Possible duplicate of [Why is C++ numeric\_limits::max() == 0?](http://stackoverflow.com/questions/9201865/why-is-c-numeric-limitsenum-typemax-0) – awesoon Feb 23 '16 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? – Bruce Adams Feb 23 '16 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 Feb 23 '16 at 11:59
  • I think this is another archaic underexploitation of the language features. (like http://stackoverflow.com/questions/21936507/why-isnt-stdarraysize-static) perhaps what you are supposed to do replace `std::numeric_limits::max()` by `std::numeric_limit::is_specialized::value?std::numeric_limits::max():throw 0;' (or some clever compile time equivalent.) – alfC Mar 16 '16 at 20:35
  • By the way, `std::numeric_limits::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::min()` which is an inconsistent mess for floating and integer types.) – alfC Mar 16 '16 at 20:48

2 Answers2

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.

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • 2
    I see it in the *limits* header now. But why does the default version of numeric_limits::max() use the default constructor. Surely it would be better to have a compiler error here? – Bruce Adams Feb 23 '16 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. – TartanLlama Feb 23 '16 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. – Bruce Adams Feb 23 '16 at 11:45
  • 3
    @Bruce - The `` 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 Mar 16 '16 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. – Bruce Adams Mar 16 '16 at 22:27
11

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.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182