1 Difficulties with sizeof
sizeof is an operator inherited from C, used to obtain the size (in bytes) of an object in memory. There are several issues when using sizeof; first, it returns the number of bytes of the object, and when dealing with arrays, it does not return the expected number of elements. In many open-source codes, you will see macros like this:
#define count_of(x) (sizeof(x)/sizeof((x)[0]))
This macro provides a workaround to obtain the number of elements in an array, but having every individual or team reinvent this wheel is clearly not a good idea. Secondly, sizeof mechanically retrieves the size of the object; for pointers, it returns the size of the pointer itself, not the size of the object it points to. This means that the count_of macro will yield incorrect results when dealing with dynamically allocated arrays:
int* pa = new int[10]; auto count = count_of(pa); // 1 or 2, but not the expected 10
Finally, sizeof is also ineffective when dealing with containers from the C++ standard library; to get the size of a container, you must call the container’s own size() member function.
2 std::size() (C++ 17)
C++ 17 introduced a set of non-member data manipulation functions [References 1-3], including std::data(), std::empty(), and std::size(). std::size() supports native arrays and container classes with a size() member (not necessarily standard library containers). However, std::size() still does not support dynamic arrays, but the benefit is that it will throw an error directly for dynamic arrays, which is better than sizeof silently giving you an incorrect result.
std::size() also has an “obvious” issue: its return value is always std::size_t, which is an unsigned type. When compared with signed types, this can be a hidden bomb. For example, consider this code:
std::vector<int> v = { 1, 2, 3 };for (auto i = std::size(v) - 1; i >= 0; --i) { std::cout << v[i] << " ";}
Can you spot the problem?
3 std::ssize() (C++ 20)
Why the extra ‘s’? Is it because the identifier size is already taken? Not really; the extra ‘s’ indicates that this is a signed size() function. The criticism of the standard library’s container size() member returning an unsigned size_t type is not new, and the std::size() function merely continued this trend. However, returning an unsigned value has always been a pain, and finally, C++ 20 introduced the std::ssize() function [4], which provides a type conversion for the size() function.
Why did it take until C++ 20 to provide this remedy? It relates to std::span. std::span was also introduced in C++ 20, and its size can be a compile-time known size or dynamic_extent. During the push for std::span to enter the standard, its size and index used signed integers (index_type), with -1 as a sentinel for size. However, all standard library containers’ sizes are unsigned size_t, so for span to enter the standard library, it had to conform to the standard library’s rules. Ultimately, [References 4,5] adopted the unsigned approach, but the ssize() member function was rejected.
If span were to have an ssize() member function, would all containers need to add an ssize() function? Clearly, there was significant opposition, so providing a standalone std::ssize() function was relatively easier to accept. The std::ssize() function, like std::size(), only supports containers and native arrays, but it provides a mechanism for returning signed results. In examples similar to section 2, using the std::ssize() function can avoid issues arising from unsigned integer overflow.
References
[1] N4017: Non-member size() and more
[2] N4155: Non-member size() and more (Revision 1)
[3] N4280: Non-member size() and more (Revision 2)
[4] P1227: Signed ssize() functions, unsigned size() functions (Revision 2)
[5] P1089R2: Sizes Should Only span Unsigned
[6] https://en.cppreference.com/w/cpp/iterator/size.html
Information, Code, and Previous ArticlesC++ Latch and BarrierC++ SemaphoreC++ lock() and try_lock() FunctionsC++ Various LocksC++ Various std::mutexC++ SpanC++ Copy ElisionC++ Time Library Part 8: Format and FormattingC++ 20 PIC++ [[noreturn]] SpecifierC++ How to Declare Lambda as FriendC++ Cache Line InterfaceC++ Clamp FunctionC++ Three and Five RulesC++ Strict Aliasing RulesC++ construct_at FunctionContent and Notification Summary