When to Use Std::Size_T

When to use std::size_t?

A good rule of thumb is for anything that you need to compare in the loop condition against something that is naturally a std::size_t itself.

std::size_t is the type of any sizeof expression and as is guaranteed to be able to express the maximum size of any object (including any array) in C++. By extension it is also guaranteed to be big enough for any array index so it is a natural type for a loop by index over an array.

If you are just counting up to a number then it may be more natural to use either the type of the variable that holds that number or an int or unsigned int (if large enough) as these should be a natural size for the machine.

Why do I need to use `size_t` in C++?

Its main advantage is that it's the right tool for the job.

size_t is literally defined to be big enough to represent the size of any object on your platform. The others are not. So, when you want to store the size of an object, why would you use anything else?

You can use int if you like, but you'll be deliberately choosing the inferior option that leads to bugs. I don't quite understand why you'd want to do so, but hey it's your code.

If you choose to use float, though, please tell us what program you're writing so we can avoid it. :)

What to do with size_t vs. std::size_t?

You should specify it with the using directive.

using std::size_t;

Add it either to the global scope of each compilation unit, or to the local scopes if it would cause interference at the global scope.

stddef.h also works, as you noted, and honestly that method is not "worse" than this one. However, stddef.h is a backwards compatibility header, and it might be best to avoid relying on it in new code.

I prefer the using directive because it does not pollute the global namespace anywhere you don't need to, and does not rely on arbitrary compiler support of nonstandard behavior. Further, this is the generally accepted way to bring a type into a namespace when multiple options are otherwise possible, so it is not unique to the usage of size_t.

This isn't really something that a person can authoritatively answer. I've been a professional developer for 10 years, and have worked with C++ since 1998, but I will never see any statistically significant portion of the total C++ code that's been written. From what I have seen, there is plenty of code out there that still uses stddef.h, and it won't likely break anytime soon.

For new code, I prefer just typing the "std::" prefix everywhere, only applying the using directives when it becomes cumbersome or difficult to read. However, I recognize that this can be irritating for "inherited" code, which is where the file-scope using directives are better. If you have the time to properly refactor the inherited code, there's a good argument that you should do so, but it's very likely to involve more than just the size_t variables.

I should also mention that the C++ FAQ (item 27.5) mentions this concern as well here, where I got the impression they mostly recommend consistency with others on your team.

I want to note here that it is NOT good practice to apply "using namespace std" at the file scope, though this would also bring size_t into the global namespace. I will link the reason for that here.

I seem to have scared tuple_cat off (sorry), but I really did think his empirical method was good, so I'm trying to incorporate some changes to resolve my concerns with his answer. I tried searching github with the following modified queries, which admittedly still may have some issues:

A) "size_t" AND "stddef.h" language:c++
B) "std::size_t" AND "<cstddef>" language:c++
C) "size_t" AND "<cstddef>" AND NOT "std::size_t" language:c++
D) "size_t" AND "<cstddef>" AND "using namespace std" AND NOT "std::size_t" language:c++
E) "size_t" AND "<cstddef>" AND "using std::size_t" language:c++

I get the following:

  • A) 974,239 results (stddef.h approach)
  • B) 1,230,021 results (cstddef approach, with "std::" prefixes)
  • C) 469,721 results (cstddef approach, no prefixes)
  • D) 32,539 results (cstddef approach, "using namespace std", DON'T DO THIS!)
  • E) 27,080 results (method I recommend, "using std::size_t")

It's definitely not perfect, and I welcome criticism to make it better, but it appears that the method I recommend, as stated, is not the most popular. Based on the data, it appears that the most popular is using the "std::" prefixes on size_t (B), followed by including "stddef.h" (A). Luckily, the bad approach of (D) is not popular, but it appears that many people may be relying on other files/headers to bring size_t into the global namespace, or just hoping it's already there on the compiler (C).

Therefore, to "go with the herd", you should prepend everything with "std::". If you don't want to do that, then "stddef.h" is also in very common use, but my preference is still the using directive.

std::size_t vs. size_type as parameters and function return types

but of course there's the off-chance that the size of the vector isn't of type std::size_t

Such off-chance doesn't practically exist in this case because std::vector<MyClass*>::size_type is (indirectly guaranteed and required to be) of type std::size_t. Using std::size_t is fine in this case, and it doesn't leak unnecessary implementation details.


In the case of standard containers, Container::size_type is defined based directly on on what allocator is being used. Thus, using size_type is typically only necessary when the allocator type - or the container type itself - is templated. In the allocator case, you can use allocator traits instead of the container member type which allows you to keep the container type hidden. If the container type itself is templated, then there is no point in hiding it since only someone who knows the container could have instantiated the template in the first place.

Furthermore, you can hide - or rather obfuscate (in a positive, encapsulating way) - the function declaration by creating a type alias member, just like std::vector has a type alias member based on its allocator.

Example:

template<class Alloc>
class Foo
{
// could be hidden with PIMPL if desired
std::vector<MyClass*, Alloc> m_myVector;

public:
// Since C++11
using size_type = typename std::allocator_traits<Alloc>::size_type;
// Prior to C++11
typedef typename Alloc::size_type size_type;

size_type size();
};

Does std::size_t make sense in C++?

There seems to be confusion among the stackoverflow crowd concerning this

::size_t is defined in the backward compatibility header stddef.h . It's been part of ANSI/ISO C and ISO C++ since their very beginning. Every C++ implementation has to ship with stddef.h (compatibility) and cstddef where only the latter defines std::size_t and not necessarily ::size_t. See Annex D of the C++ Standard.

Is it good practice to use std::size_t all over the place?

std::size_t is the type to use for counting memory locations, like array lengths, the size of objects, etc. STL containers are using container_type::size_type, which will usually map to std::size_t, but isn't guaranteed to do so.

If you need types for holding non-negative integrals which are not used for the above mentioned purposes, what's wrong with unsigned short, unsigned int, and unsigned long?

Should I always use size_t when indexing arrays?

size_t is an unsigned integer that is capable of holding the size of the largest object you can allocate. It is useful for indexing because this means it can index into the largest array you can allocate.

This does not mean it is required or even necessarily recommended for indexing. You can use any integer type that is large enough to index the array. int_fast32_t might be faster, uint_least16_t might be smaller in a structure, and so on. Know your data, and you can make a good choice.

One consideration you should make is that on some platforms, using a signed index might require an extra sign extension instruction. As an example, here is x86-64:

// ; zero-extending idx (in edx) is "free" by simply using rdx.
// movzx eax, BYTE PTR [rcx+rdx]
// ret
char get_index(char *ptr, unsigned idx)
{
return ptr[idx];
}

// ; sign extending idx from 32 bits to 64 bits with movsx here.
// movsx rdx, edx
// movzx eax, BYTE PTR [rcx+rdx]
// ret
char get_index(char *ptr, int idx)
{
return ptr[idx];
}

Virtual memory is outside the scope of C or C++. From their point of view, you simply index into memory and it's up to your platform to make it work. In practice your app only uses virtual addresses; your CPU/OS is translating the virtual address to a physical address behind the scenes. It is not something you need to worry about.

Why can I use size_t and std::size_t in MSVS without the traditional headers?

Strictly speaking your code is illegal. The size_t type is required to be declared in the following headers:

<cstddef>
<cstdio>
<cstdlib>
<cstring>
<ctime>
<cwchar>

But also the standard allows standard headers to include other standard headers. So most likely the header <vector> in the standard library used by MSVC includes one of the headers above. This is allowed, but not mandated by the standard so this will work on your setup, but can fail to work on other standard library implementations, even on a future version of the same one you are using.

So in conclusion try to include all the headers required by the standard for all the definitions you are using.

IMHO this is a faulty behavior, but is the required price C++ pays for backward compatibility to the inclusion system that seemed a reasonable design many years ago. The limitations and drawbacks of this are well known today and so the committee is working on modules, which is a modern alternative to the current inclusion mechanism.


As to why you can use size_t without std:::

<cstddef> is required to declare std::size_t and may also optionally declare (or bring in the declaration of) size_t in the global scope.

<stddef.h> is a C backward compatible header and it declares size_t at the global scope.

So either <cstddef> declares size_t at a global level and is included by <vector> or <stddef.h> is included by <vector> - most likely indirectly via <cstddef>.

https://stackoverflow.com/a/283023/2805305

size_t vs int in C++ and/or C

In general, size_t should be used whenever you are measuring the size of something. It is really strange that size_t is only required to represent between 0 and SIZE_MAX bytes and SIZE_MAX is only required to be 65,535...

The other interesting constraints from the C++ and C Standards are:

  • the return type of sizeof() is size_t and it is an unsigned integer
  • operator new() takes the number of bytes to allocate as a size_t parameter
  • size_t is defined in <cstddef>
  • SIZE_MAX is defined in <limits.h> in C99 but not mentioned in C++98?!
  • size_t is not included in the list of fundamental integer types so I have always assumed that size_t is a type alias for one of the fundamental types: char, short int, int, and long int.

If you are counting bytes, then you should definitely be using size_t. If you are counting the number of elements, then you should probably use size_t since this seems to be what C++ has been using. In any case, you don't want to use int - at the very least use unsigned long or unsigned long long if you are using TR1. Or... even better... typedef whatever you end up using to size_type or just include <cstddef> and use std::size_t.



Related Topics



Leave a reply



Submit