What does std::numeric_limits do for types that don't specialize it?

197 Views Asked by At

This code compiles and runs

#include <limits>
#include <iostream>

struct Foo {
    int x;
};

static_assert(!std::numeric_limits<Foo>::is_specialized);

int main() {
    std::cout << "---" << std::endl;
    std::cout << std::numeric_limits<Foo>::lowest().x << std::endl;
    std::cout << std::numeric_limits<Foo>::min().x << std::endl;
    std::cout << std::numeric_limits<Foo>::max().x << std::endl;
    std::cout << "---" << std::endl;
}

and it prints

---
0
0
0
---

Where are those numbers coming from?

On cppreference I read that

This information is provided via specializations of the std::numeric_limits template. The standard library makes available specializations for all arithmetic types

from which I'd deduce that the absence of a specialization for a given type Foo means that I'm not allowed to use it for Foo. So is the above output just a manifestation of UB? Or what?

If I change int to char in the code above, the output is

---
---

which to me is "What?! Where have the std::endls gone, to start with???"

2

There are 2 best solutions below

1
user17732522 On BEST ANSWER

The primary template of std::numeric_limits value-initializes all members or return values from member functions, see [numeric.limits.general]/3. So that is what you get if the template isn't specialized for the provided type. There is no UB.

Any specialization should set is_specialized to true and must define all members, with sensible values where applicable, or 0 or false otherwise, see [numeric.limits.special]/1 and [numeric.limits.general]/4.

Interestingly [numeric.limits.general]/4 requires the standard specializations to set is_specialized to true, but I don't see any equivalent requirement for user specializations.

0
eerorika On

Using the unspecialized numeric_limits is well defined. The code example has no UB. Quote from the standard draft:

...
static constexpr T min() noexcept { return T(); }
static constexpr T max() noexcept { return T(); }
static constexpr T lowest() noexcept { return T(); }
...

For the numeric_limits primary template, all data members are value-initialized and all member functions return a value-initialized object.

[Note 1: This means all members have zero or false values unless numeric_limits is specialized for a type. - end note]

- [numeric.limits.general] p3