Привет, дорогой читатель, меня зовут Ксавье Жувено, и в этой статье мы поговорим о том, как проверять ограничения типов в C++. Этот пост был навеян правилом из первой главы Code Craft Пита Гудлиффа Защитное программирование.

Зачем проверять ограничения типов?

Давайте начнем с разговора о том, почему и когда мы должны проверять ограничение типа. Первый ответ, который приходит мне на ум, это потому что мы можем 😝

Но есть конкретное использование этого 😉

Самый распространенный, о котором я могу думать, это когда вы хотите преобразовать переменную из одного типа в другой. В большинстве случаев оба типа будут иметь разные ограничения, поэтому добавление утверждения, чтобы убедиться, что значение переменной правильно определено в целевом типе, может быть хорошей привычкой защитного программирования.

Другой случай, когда вы можете захотеть сделать это, — избежать неопределенного поведения или использовать в своих интересах определенное поведение переполнения для некоторых типов. Таким образом, вы можете захотеть избежать переполнения целочисленной переменной, если вы слишком сильно увеличиваете ее, поскольку это неопределенное поведение, но, с другой стороны, вы можете намеренно переполнить беззнаковую переменную следить за работой вашего процессора при выполнении встроенных программ.

Все эти случаи связаны с ограничениями типов. А если у вас есть другие примеры, с удовольствием прочту их в комментариях к этой статье 😄

Ограничения в стиле C: переменные препроцессора

Если у вас есть программа на C или на старом C++ (или даже на новом C++, плохо не отвечающем стандарту), вы могли столкнуться с той переменной препроцессора, которая хранит ограничения типов std.

Они выглядят как INT_MIN и INT_MAX для целочисленного типа или FLT_MIN и FLT_MAX для типа с плавающей запятой. Существуют также другие константы, такие как FLT_EPSILON, которые представляют собой разницу между двумя числами с плавающей запятой.

Полный список вы можете найти здесь. Эти константы полезны, когда они нужны вам в интерфейсе C или при программировании на C, но если вы используете C++, я рекомендую вам использовать следующую технику 😉

Ограничения С++: std::numeric_limits

В стандартном C++ есть шаблон класса, который предоставляет ограничения и дополнительную информацию о типе числовых типов std. Этот класс называется numeric_limits. При желании вы можете найти полную документацию здесь.

Его можно использовать следующим образом:

#include <limits>
constexpr auto int_upper_limit = std::numeric_limits::max();

У этого класса есть несколько преимуществ.

Прежде всего, он предоставляет вам единый интерфейс для ограничений всех типов, что означает, что его можно без проблем использовать внутри шаблона класса или функции, в то время как с решением в стиле C это будет намного сложнее.

#include <limits>
template <typename T>
class MyClass
{
  void someMethod() {
    constexpr auto variable = std::numeric_limits<T>::max();
    // ...
  }
}; 

Кроме того, синтаксис намного понятнее, чем решение в стиле C, и близок к английскому языку. 🤓

Наконец, его можно расширить на ваши собственные типы, чтобы все типы вашей программы могли использовать один и тот же интерфейс, и это то, что мы собираемся сделать сейчас. 😃

Создадим собственные ограничения типов

Как упоминалось в предыдущей части, огромным преимуществом std::numeric_limits является то, что его можно использовать с вашими собственными типами, так что все типы вашей программы будут иметь одинаковый интерфейс при поиске ограничений на них. Вот как вы можете сделать это по максимуму:

#include <limits>
struct my_type;
template <> class std::numeric_limits {
public:
  static constexpr int max() noexcept { return 1; }
};
int main() {
  constexpr auto my_type_upper_limit = std::numeric_limits<my_type>::max();
  static_assert(my_type_upper_limit == 1, "Error - the limit is not well set");
  return 0;
}

Давайте подробно рассмотрим, как работает этот пример. 🙂 Во-первых, мы указываем наш тип, названный здесь my_type, чтобы можно было создать специализацию для шаблона класса std::numeric_limits.

В специализации класса шаблона мы создаем метод max, который следует сигнатуре, определенной стандартом. Вы можете, например, найти прототип метода max здесь, а посмотреть другие методы, которые можно указать в std::numeric_limits здесь.

Наконец, в функции main мы вызываем метод max, используя std::numeric_limits с нашим собственным типом. И вуаля! Теперь у нас есть тип, который может использовать тот же интерфейс, что и стандартный для своих ограничений! 😄

Конечно, здесь мы указали только метод max, но вам, вероятно, также потребуется указать методы min и lowest. Это можно сделать так же, как мы сделали для метода max. 😉

Вывод

Я здесь не для того, чтобы начинать дискуссию о том, что лучше C или C++.

Если вы используете C, используйте способ C для проверки ограничений ваших типов. И если вы используете C++, то используйте для этого способ C++, это поможет вам поддерживать некоторую согласованность в том, как вы проверяете ограничения типов в своем коде.

Я хочу сказать, что если вы хотите интегрировать защитное программирование (а вы должны это сделать), вам нужно будет проверить ограничения вашего типа, и теперь вы знаете, как это сделать на C++. 😄

Спасибо всем за прочтение этой статьи, и до моей следующей статьи, хорошего дня 😉

Первоначально опубликовано на http://10xlearner.com 8 января 2020 г.