3923. The specification of numeric_limits doesn't clearly distinguish between implementation requirements and user requirements

Section: 17.3.5.1 [numeric.limits.general] Status: New Submitter: Daniel Krügler Opened: 2023-04-15 Last modified: 2023-05-24

Priority: 3

View other active issues in [numeric.limits.general].

View all other issues in [numeric.limits.general].

View all issues with New status.

Discussion:

The wording of 17.3.5.1 [numeric.limits.general] seemingly has not been gone through a similar thorough rewording review which we performed in the past to clean-up the working draft as we did via the series of "Mandating" papers by Marshall Clow (P1458 - P1465 and even more).

17.3.5.1 [numeric.limits.general] contains several nowadays inappropriate wording forms, which don't distinguish well enough between requirements imposed on implementations (Where we shouldn't use "shall" wording in the ambiguous form of "Specializations shall be provided for each arithmetic type") and requirements imposed on user types, this has also caused confusion as expressed in LWG 3922. It is "obvious" that a program is intended to be allowed to provide program-defined specializations, but as LWG 3922 points out, it is unclear how such a specialization is able to meet the requirement "(b) the specialization meets the standard library requirements for the original template" specified in 16.4.5.2.1 [namespace.std] p2.

Another problem is the usage of the unclear wording "Non-arithmetic standard types", which should be replaced by a more precise wording form.

An additional problem is that we actually already do require an implementation to provide specializations for the (library-provided) integer-class types (24.3.4.4 [iterator.concept.winc]), so contrary to what p6 says, we already have at least one exception, where the library is required to specialize numeric_limits for a non-arithmetic type. We should make that a bit clearer here as well.

This issue is related to LWG 3922 and paper P1841.

[2023-05-24; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4944.

[Drafting Note: This wording would also solve LWG issue 3922 under the assumption that option A is intended]

  1. Modify 17.3.3 [limits.syn], header <limits> synopsis, as indicated:

    […]
    // 17.3.5.1 [numeric.limits.general], class template numeric_limits
    template<class T> class numeric_limits;
    
    // 17.3.5.3 [numeric.special], numeric_limits specializations
    template<class T> class numeric_limits<const T>;
    template<class T> class numeric_limits<volatile T>;
    template<class T> class numeric_limits<const volatile T>;
    
    template<> class numeric_limits<bool>;
    […]
    
  2. Modify 17.3.5.1 [numeric.limits.general] as indicated:

    -1- The numeric_limits class template provides a C++ program with information about various properties of the implementation's representation of the arithmetic types.

    […]

    [Drafting Note: It is unclear whether the requirement in the following paragraph 2 is intended to apply to program-defined specializations as well. Consider as an example a user-defined arithmetic-like type that provides arbitrary precision arithmetic which may require dynamic memory for certain object constructions. Is it invalid to specialize numeric_limits for such a type or may the program-defined specialization deviate from this requirement for at least some of its members?

    If we want to make this restriction relaxed for program-defined specializations, further wording would be needed to give that permission]

    -2- For all members declared static constexpr in the numeric_limits template, specializations shall define these values in such a way that they are usable as constant expressions.

    -3- 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]

    -4- An implementation shall provide sSpecializations shall be provided for each arithmetic type, both floating-point and integer, including bool. The member is_specialized shall beis true for all such specializations of numeric_limits.

    -5- 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.

    [Drafting Note: If we had introduced numeric_limits today we would likely have only allowed to provide specializations for cv-unqualified program-defined types, but that ship has sailed long ago.

    Interestingly currently there doesn't exist a specification that defines under which situations the static member is_specialized should be defined true or false (This touches LWG 205). The wording below does not attempt to improve that situation, but at least clarifies that its value may be different from that of the primary template.

    Note that this proposed wording does — opposed to the approach of LWG 3922 — not restrict that specializations can only be provided by program-defined types "emulating an arithmetic type", because that would break existing specializations and is also problematic in the light of the non-existing definition of that term.

    The below wording strategy gives permission to specialize numeric_limits only for non-array object types. An alternative approach could make it a precondition instead to instantiate the template for non-array object types, for example.]

    -?- A program may specialize the numeric_limits template for a program-defined non-array object type. Such a specialization is permitted to define a value for any static member that differs from what the primary template would have defined, as appropriate for that type.

    [Note: It still has to meet the general requirements specified in subclause 17.3.5.1 [numeric.limits.general] and subclause 17.3.5.3 [numeric.special]end note].

    [Drafting Note: The following restriction is carefully drafted to ensure that a library has the freedom to provide such specializations for "extended" types (That are not necessarily integer-class types). The restriction is intended to apply only to "official" (strict) C++ standard library types]

    -6- An implementation shall not provide specializations for nNon-arithmetic standard types of the C++ standard library, such as complex<T> (29.4.3 [complex]), unless specified otherwise (e.g. for integer-class types, 24.3.4.4 [iterator.concept.winc]), shall not have specializations.

  3. Modify 17.3.5.3 [numeric.special] as indicated:

    [Drafting Note: I have left the "shall" usage in p1, because this seems to be a requirement for program-defined specializations as well. The second sentence of p1 is one of the funny ones which partially look like introductory wording, but also seems to indicate requirements, albeit specified in an unusual way ("meaningful").

    The extra wording added after p2 below attempts to improve the wording situation caused by LWG 559 and does that by following a similar approach as done in 22.4.7 [tuple.helper]. ]

    -1- All members shall be provided for all specializations. However, many values are only required to be meaningful under certain conditions (for example, epsilon() is only meaningful if is_integer is false). Any value that is not "meaningful" shall be set to 0 or false.

    -2- [Example 1:

    […]

    end note]

    template<class T> class numeric_limits<const T>;
    template<class T> class numeric_limits<volatile T>;
    template<class T> class numeric_limits<const volatile T>;
    

    -?- Let NL denote numeric_limits<T> of the cv-unqualified type T. Then the value of each member of these specializations of numeric_limits is equal to the value of the corresponding member of the specialization NL.

    -3- The specialization for bool shall beis provided as follows:

    […]