Почему std :: ssize() введен в С++ 20?

С++ 20 представил свободную функцию std::ssize() как std::ssize() ниже:

template <class C>
    constexpr auto ssize(const C& c)
        -> std::common_type_t<std::ptrdiff_t,
                              std::make_signed_t<decltype(c.size())>>;

Кажется, возможная реализация использует static_cast для преобразования возвращаемого значения функции-члена size() класса C в ее подписанный аналог.

Поскольку функция-член size() в C всегда возвращает неотрицательные значения, зачем кому-то хотеть хранить их в знаковых переменных? В случае, если кто-то действительно хочет, это просто static_cast.

Почему std::ssize() введен в С++ 20?

Ответ 1

Обоснование описано в http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1227r1.html. Цитата:

Когда span был принят в С++ 17, он использовал целое число со знаком, как индекс, так и размер. Частично это должно было позволить использовать "-1" в качестве дозорного значения для указания типа, размер которого не был известен во время компиляции. Но наличие контейнера STL, функция size() которого возвращала значение со знаком, было проблематичным, поэтому для устранения проблемы был введен протокол P1089. Он получил поддержку большинства, но не от 2 до -1 запаса, необходимого для достижения консенсуса.

В этом документе, P1227, было предложено добавить функции, не являющиеся членами std :: ssize и member ssize(). Их включение сделало бы определенный код намного более простым и позволило бы избежать нежелательных вычислений без знака в размерах. Идея заключалась в том, что сопротивление P1089 уменьшилось бы, если бы ssize() был доступен для всех контейнеров, как через std :: ssize(), так и в качестве функций-членов.

Ответ 2

Безвозмездно украден из Eric Niebler:

'Unsigned types signal that a negative index/size is not sane' было превалирующим мнением при разработке STL. Но логически, количество вещей не должно быть положительным. Я могу захотеть сохранить счет в целом числе со знаком, чтобы обозначить количество элементов, добавленных в коллекцию или удаленных из нее. Тогда я хотел бы объединить это с размером коллекции. Если размер коллекции не подписан, теперь я вынужден смешивать арифметику со знаком и без знака, которая является ошибочной фермой. Об этом предупреждают компиляторы, но поскольку дизайн STL в значительной степени заставляет программистов попадать в эту ситуацию, предупреждение настолько распространено, что большинство людей его отключают. Это позор, потому что это скрывает настоящие ошибки.

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

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