45

The documentation of std::numeric_limits<T> says it should not be specialized for non-fundamental types. What about number-like user-defined types? If I define my own type T which represents a numeric value and overloads numeric operators, and for which the information represented by numeric_limits makes sense -- will anything break if I specialize numeric_limits for that type?

danielschemmel
  • 10,885
  • 1
  • 36
  • 58
user410315
  • 696
  • 5
  • 8

2 Answers2

36

Short answer:

Go ahead, nothing bad will happen.

Long answer:

The C++ standard extensively protects the ::std namespace in C++11 17.6.4.2.1, but specifically allows your case in paragraphs 1 and 2:

The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

[...] A program may explicitly instantiate a template defined in the standard library only if the declaration depends on the name of a user-defined type and the instantiation meets the standard library requirements for the original template.

The older C++03 has a similar definition in 17.4.3.1/1:

It is undefined for a C++ program to add declarations or definitions to namespace std or namespaces within namespace std unless otherwise specified. A program may add template specializations for any standard library template to namespace std. Such a specialization (complete or partial) of a standard library template results in undefined behavior unless the declaration depends on a user-defined name of external linkage and unless the specialization meets the standard library requirements for the original template.

After getting past this fundamental stepping stone, you already pointed out, C++03 18.2.1/4 forbids specializations of ::std::numeric_limits for certain types:

Non-fundamental standard types, such as complex (26.2.2), shall not have specializations.

The more current C++11 18.3.2.1/4 has a slightly different wording:

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

Both of these formulations however allow specializations for non-standard types, which T is, since you defined it yourself (as @BoPersson already pointed out in the comments).

Caveats

C++11 18.3.2.3/1 hints that you should (but does not require you to) ensure that your specialization has all members.

Also, you may wish to ensure that C++11 18.3.2.3/2 is not violated by your specialization:

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.

Which essentially means, that if you wish to specialize it for T, you should also do so for T const, T volatile and T const volatile.

danielschemmel
  • 10,885
  • 1
  • 36
  • 58
  • To me, this reads like a contradiction to 17.6.4.2.1/2: *"The behavior of a C++ program is undefined if it declares an explicit specialization of any member function of a standard library class template"*. What am I missing? – IInspectable Mar 05 '14 at 02:54
  • This is not about explicitly specializing a **member function**, but the whole class template. – danielschemmel Mar 05 '14 at 23:41
  • 4
    "Such a specialization [...] results in undefined behavior unless [...] the specialization meets the standard library requirements for the original template." The original requirements only apply to arithmetic types, suggesting that user specializations may have undefined behavior. – Richard Smith Oct 24 '14 at 16:32
  • @RichardSmith If you can substantiate the fact that the original requirements have an implicit "must be an arithmetic type" somewhere, I might be inclined to believe you. However, as far as I remember (the last edit to this question was may '13), nothing in the standards mandates this. – danielschemmel Oct 31 '14 at 18:42
  • Please explain why one of the many template argument resolution mechanisms wouldn't use a specialization of `std::numeric_limits` for instantiations using cv-qualified T. – Spencer May 25 '16 at 13:59
  • @Spencer Because e.g. `int const` and `int` are two different types, which [will cause the default to be chosen](http://ideone.com/pJsvpr). There are basically no additional resolution mechanisms when passing a template type parameter directly (unlike what happens when passing an ordinary argument to a function template w/o explicitly specifying the template parameters). – danielschemmel May 30 '16 at 02:34
26

Just an example:

namespace std {
    template<> class numeric_limits<Temperature> {
    public:
       static Temperature lowest() {return Temperature(-273.15f);};
       // One can implement other methods if needed
    };
}
Yola
  • 18,496
  • 11
  • 65
  • 106
  • Excellent answer. Could you elaborate how to extend this solution for templated templated classes? E.g., if I have `template struct WrapperAroundT { T m_data; };` and I really want `numeric_limit` to apply on the underlying T – Happy Green Kid Naps Jul 04 '20 at 21:26
  • 2
    You really want this to be a `constexpr` function – user362515 Jun 22 '21 at 07:22
  • @HappyGreenKidNaps I believe you can just `template class ::std::numeric_limits< WrapperAroundT< T > > : public ::std::numeric_limits { };` and that should do it – Tim Aug 31 '23 at 09:50