С++: Почему numeric_limits работает над типами, которые он не знает?

Я создал свой собственный тип без компаратора и без специализации std::numeric_limits. Несмотря на это, по какой-то причине std::numeric_limits<MyType> компилируется отлично. Почему комитет стандартов С++ определил шаблон numeric_limits таким образом, чтобы он был действителен для всех типов, включая нечисловые типы?

Пример кода ниже:

#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;
}

Ответы

Ответ 1

Шаблон класса std::numeric_limits был добавлен в качестве замены макросов из <limits.h> до того, как метапрограммирование шаблона было чем-то: это было в предварительных стандартно распространенных черновиках (~ 1995). Мета-программирование шаблонов было изобретено Эрвином Унрухом вокруг Стокгольмской встречи (июль 1996 г.). В этот момент никто не думал, можно ли обнаружить, что шаблон класса определен. Вместо этого std::numeric_limits<T>::is_specialized указывает (во время компиляции), является ли шаблон класса специализированным и значимым для типа T. Различные члены были определены для использования разумного значения по умолчанию, которое могло бы привести к компиляции кода, хотя общий вариант был бы реализован таким образом, чтобы он не использовал ни одно из значений для типов, которые не являются специализированными.

Если std::numeric_limits указывается так же, как в стандарте С++, это не изменится без уважительной причины: любое изменение, скорее всего, сломает чей-то код, даже если этот код можно было бы улучшить с помощью обнаруженной ранее технологии (некоторые из которых был действительно недоступен с С++ 98). Комитет не будет разрабатывать такие черты, как сейчас: черты типа <type_traits> являются автономными признаками, хотя в целом все еще определены для всех жизнеспособных типов с подходящим значением по умолчанию. Тем не менее, нет никаких оснований для изменения std::numeric_limits в разрыве, поскольку текущее определение действительно работает.

Обратите внимание, что не все члены std::numeric_limits<T> допустимы для всех типов T. Например, использование std::numeric_limits<T>::max() не скомпилируется, если конструктор по умолчанию T недоступен, недоступен или delete d. Таким образом, вам лучше защищать доступ к любому из членов, независимо от того, является ли шаблон класса специализированным (с использованием С++ 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
    }
}