20

I have created my own type, without any comparator, and without a specialization of std::numeric_limits. Despite that, for some reason, std::numeric_limits<MyType> compiles fine. Why did the c++ standards committee define the numeric_limits template such that it is valid for all types, including non-numeric types??

Example code below:

#include <iostream>
#include <limits>
using namespace std;

// This is an int wrapper that defaults to 666 instead of 0
class A {
public:
    int x;
public:
    A() : x(666) {}
};

int main() {
    A a = std::numeric_limits<A>::max();
    A b = std::numeric_limits<A>::max();

    std::cout << a.x << "\n" << b.x;
    // your code goes here
    return 0;
}
5
  • 1
    Just some observations. Commented Nov 27, 2017 at 22:31
  • 1
    you just added an error by making x private. If you want an even stronger example of the problem I describe, replace class A with class A {} and delete the std::cout. The code will still compile, why? Commented Nov 27, 2017 at 22:33
  • Reword your question. As written you are asking "how come this works?" but you seem to be really asking "why did the c++ standards committee define the numeric_limits template such that it is valid for all types, including non-numeric types?" Commented Nov 27, 2017 at 22:42
  • there's a bug about it: issues.isocpp.org/show_bug.cgi?id=186. A compile-time error would be better in this case, I don't really see the benefit of the current working. Commented Nov 27, 2017 at 23:03
  • Related: stackoverflow.com/questions/16122912/… Commented Nov 27, 2017 at 23:12

1 Answer 1

16

The class template std::numeric_limits was added as a replacement for the macros from <limits.h> before template meta programming was a thing: it was in the pre-standard publicly circulated drafts (~1995). Template meta programming was invented by Erwin Unruh around the Stockholm meeting (July 1996). At this point nobody thought whether it could be detectable that a class template is defined. Instead, std::numeric_limits<T>::is_specialized would indicate (at compile-time) whether the class template is specialized and meaningful for type T. The various member were defined to use a reasonable default which would make it likely to get the code compiled although generic would be implemented such that it doesn't use any of the values for types which are not specialized.

With std::numeric_limits being specified like that in the C++ standard it wouldn't change without a very good reason: any change would likely break somebody's code - even if this code could be done better with now discovered technology (some of which was genuinely unavailable with C++98). The committee wouldn't design the traits like this now: the type traits in <type_traits> are stand-alone traits - although generally still defined for all viable types with a suitable default. However, there is also no reason to change std::numeric_limits in a breaking way as the current definition does work.

Note that not all members of std::numeric_limits<T> are valid for all types T. For example use of std::numeric_limits<T>::max() will fail to compile if T's default constructor is unaccessible, unavailable, or deleted. So, you'd be best off guarding access to any of the members on whether the class template is specialized (using C++17):

template <typename T>
void f() {
    if constexpr (std::numeric_limits<T>::is_specialized) {
        // use of std::numeric_limits<T>::max(), min(), etc.
    }
    else {
        // implement the rquired functionality differently
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Its a plausible explanation. But, what's the reason that they chose "min/max" to silently give bogus values? Why not call std::abort in this case?
@geza: That I don't know and I don't think records of proposals reach back in time to the std::numeric_limits proposal (and notes started being taken only very recently): there is pretty little rationale beyond the D&E and I don't think the D&E covers much of the library.
You can't specialize type_traits classes on user-defined types; however, you might be able to specialize numeric_limits.
I wonder what would have been the problem with just leaving the primary template undefined, though... The same way the compiler now falls back to a primary template with default values, it would then try to fall back to a primary template with no definition and error out. Wouldn't that be much saner?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.