Подписанный вариант size_t в стандартной библиотеке C ++

Есть ли подписанный вариант size_t в стандартном C ++? Это означает точно такой же размер в битах, что и size_t, но со знаком.

Конечно могу:

#include <type_traits>
using signed_size_t = std::make_signed_t<std::size_t>;

но, может быть, в стандартной библиотеке уже есть подобное определение, чтобы не придумывать лишнее имя типа?

Я знаю, что есть ssize_t и ptrdiff_t, оба подписаны. Но согласно их описанию кажется, что они оба могут иметь другой размер бит, чем size_t. Но мне нужен точно такой же размер бит, как size_t, но со знаком.


person Arty    schedule 29.12.2020    source источник
comment
Но мне нужен точно такой же размер в битах, как size_t, но со знаком. Используйте для этого static_assert: static_assert(sizeof(std::size_t) == sizeof(std::ptrdiff_t), "error message");. Это лучшее, что я знаю.   -  person NathanOliver    schedule 29.12.2020
comment
@NathanOliver. Обычно ptrdiff_t можно использовать как подписанный size_t, но вы должны проверить, чтобы убедиться?   -  person Arty    schedule 29.12.2020
comment
Также std::streamsize используется как подписанный аналог std::size_t, аналогично Тип POSIX ssize_t.   -  person dxiv    schedule 29.12.2020
comment
По моему опыту, оба типа имеют одинаковый размер. Это не гарантируется, но с статическим утверждением вы в основном это гарантируете.   -  person NathanOliver    schedule 29.12.2020
comment
ssize_t не является стандартным C ++; это POSIX. Но на самом деле, если вам нужна подписанная версия size_t, то по-вашему, все в порядке. Я бы предположил, что стандартного нет, потому что отрицательных размеров на самом деле не существует, поэтому стандарт предпочитает более значимые имена, такие как ptrdiff_t, когда ему нужны такие вещи.   -  person HTNW    schedule 29.12.2020
comment
Есть std::ssize, но он может быть больше size_t.   -  person user975989    schedule 30.12.2020


Ответы (1)


Есть одно место, где подписанная версия std::size_t (а также неподписанная версия std::ptrdiff_t) встречается в стандарте: спецификатор формата printf %zu предназначен для объектов std::size_t. %zd предназначен для объектов, как указано в стандарте C, на который ссылается стандарт C ++, соответствующего целочисленного типа со знаком [из std::size_t]

std::printf("%zu %zd %td %tu",
    std::size_t{0}, std::make_signed_t<std::size_t>{0},
    std::ptrdiff_t{0}, std::make_unsigned_t<std::ptrdiff_t>{0}
);

И поскольку не существует типа, специально названного для %zd и %tu, я склонен полагать, что не существует стандартного имени, как вы хотите (кроме как std::make_signed_t<std::size_t>).


Кроме того, нет особых причин хотеть подписанный вариант std::size_t: std::size_t предназначен для размера объекта, а размер объекта не подписан.

ssize_t гарантированно будет содержать либо -1, либо неотрицательное значение. Гарантированный диапазон равен [-1, SSIZE_MAX] (и это тип, специфичный для POSIX, а не стандартный тип C ++). Это потому, что он используется для значения без знака или -1 в случае ошибки.

В стандартной библиотеке C ++ для этого просто используется std::size_t, а вместо std::size_t(-1) == SIZE_MAX для указания ошибки / специального значения (см .: std::basic_string<...>::npos, std::dynamic_extent), поэтому вы можете просто использовать std::size_t вместо ssize_t, если вам нужно значение ошибки (или, может быть, std::optional<std::size_t>).


Если вместо этого вы хотите, чтобы что-то представляло размер, но было подписано, std::ssize(c) (подписано size) вернет std::common_type_t<std::ptrdiff_t, std::make_signed_t<decltype(c.size())>>. Для типов массивов std::ssize возвращает std::ptrdiff_t. Так что, наверное, для этой цели используйте std::ptrdiff_t.


Если вам нужен тип, используемый для представления расстояния между двумя итераторами (включая указатели), для этого был создан std::ptrdiff_t. Это в основном совпадает с концепцией размеров со знаком, и std::iterator_traits<...>::difference_type обычно std::ptrdiff_t.


Это не означает, что sizeof(std::ptrdiff_t) == sizeof(std::size_t). Стандарт не определяет никаких отношений между ними. Оба sizeof(std::ptrdiff_t) < sizeof(std::size_t) и sizeof(std::ptrdiff_t) > sizeof(std::size_t) кажутся теоретически возможными, но я не нашел ни одной системы, где бы это было так. Таким образом, простое утверждение должно работать на всех платформах и позволять вам просто использовать std::ptrdiff_t:

static_assert(
    sizeof(std::size_t) == sizeof(std::ptrdiff_t) &&
    static_cast<std::size_t>(std::numeric_limits<std::ptrdiff_t>::max()) == std::numeric_limits<std::size_t>::max() / 2u,
    "ptrdiff_t and size_t are not compatible"
);

(Во многих системах std::size_t равно unsigned int, а std::ptrdiff_t равно signed long, но sizeof(int) == sizeof(long), поэтому мы должны проверять диапазоны типов, а не std::is_same_v<std::ptrdiff_t, std::make_signed_t<std::size_t>>)

Или просто используйте std::make_signed_t<std::size_t>, как вы уже сделали.

person Artyer    schedule 29.12.2020