Reputation: 16747
Is there a signed variant of size_t in standard C++? Meaning exactly same bit size as size_t but signed.
Of course I can do:
#include <type_traits>
using signed_size_t = std::make_signed_t<std::size_t>;
but maybe there is already similar definition in standard library, not to invent extra type name?
I know there are ssize_t and ptrdiff_t, both signed. But according to theirs description it seems that they can both be of different bit size than size_t. But I need exactly same bit size as size_t but signed.
Upvotes: 8
Views: 2707
Reputation: 40811
There is one place where the "signed version of std::size_t
" (and also the unsigned version of std::ptrdiff_t
) comes up in the standard: The printf
format specifier %zu
is for std::size_t
objects. %zd
is for objects of, as the C standard which the C++ standard refers to says, "the corresponding signed integer type [of 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}
);
And since there is no type specifically named for %zd
and %tu
, I'm inclined to believe there is no standard name like you want (other than as std::make_signed_t<std::size_t>
).
As an aside, there is not much reason to want a signed variant of std::size_t
: std::size_t
is for the size of an object, and an object's size isn't signed.
ssize_t
is only guaranteed to hold either -1
or a non-negative value. It's guaranteed range is [-1, SSIZE_MAX]
(And is a POSIX-specific type, not a standard C++ type). This is because it's used for "an unsigned value or -1 on error".
The C++ standard library just uses std::size_t
for this, with std::size_t(-1) == SIZE_MAX
instead to indicate the error/special value (See: std::basic_string<...>::npos
, std::dynamic_extent
), so you can just use std::size_t
instead of ssize_t
if you wanted an error value (or maybe std::optional<std::size_t>
)
If you instead wanted "something to represent a size but is signed", std::ssize(c)
("signed size
") returns std::common_type_t<std::ptrdiff_t, std::make_signed_t<decltype(c.size())>>
. For array types, std::ssize
returns std::ptrdiff_t
. So probably use std::ptrdiff_t
for this purpose.
If you wanted "the type used to represent the distance between two iterators" (including pointers), std::ptrdiff_t
was made for this. This mostly coincides with the concept of signed sizes, and std::iterator_traits<...>::difference_type
is usually std::ptrdiff_t
.
These does not mean that sizeof(std::ptrdiff_t) == sizeof(std::size_t)
. The standard does not define any relationship between them. Both of sizeof(std::ptrdiff_t) < sizeof(std::size_t)
and sizeof(std::ptrdiff_t) > sizeof(std::size_t)
seem theoretically possible, but I have not found any systems where this is the case. So a simple assertion should work on all platforms and allow you to just use 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"
);
(There are many systems where std::size_t
is unsigned int
and std::ptrdiff_t
is signed long
but sizeof(int) == sizeof(long)
, so we have to check the ranges of the types rather than std::is_same_v<std::ptrdiff_t, std::make_signed_t<std::size_t>>
)
Or just use std::make_signed_t<std::size_t>
like you already have.
Upvotes: 8