Size_T' VS 'Container::Size_Type'

size_t' vs 'container::size_type'

The standard containers define size_type as a typedef to Allocator::size_type (Allocator is a template parameter), which for std::allocator<T>::size_type is typically defined to be size_t (or a compatible type). So for the standard case, they are the same.

However, if you use a custom allocator a different underlying type could be used. So container::size_type is preferable for maximum generality.

Guarantee that std::container::size_type is a std::size_t

The approach you follow in your question seems to be the way to go, if you really need to do this.

However, I think you worry too much, since size_t can handle size_type.

If you ever find yourself in the unlikely situation where a platform implements size_t in a way that makes not big enough to contain all the values of size_type, then I am pretty sure that you will receive a compiler warning on any comparison you will attempt to perform.

'size_t' vs 'container::size_type' mentions:

The standard containers define size_type as a typedef to
Allocator::size_type (Allocator is a template parameter), which for
std::allocator is typically defined to be size_t (or a compatible
type). So for the standard case, they are the same.


So, if I were you I would be confident about my compiler to lie in the typical case. However, if I did use non-standard containers, then I would bother to follow your - as you said ugly - approach, put it in a file and let it do its job in a hidden dark corner.

Choosing between size_t and container::size_type in compile time

Following is one way to determine if a class contains a type (e.g. size_type) or not:

template <typename T> 
struct Has_size_type
{
typedef char (&yes)[2];

template <typename C> static yes test(typename C::size_type*);
template <typename> static char test(...);

static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

And following is the way to choose between 2 types:

template<bool> struct Bool;
template<typename T, typename = Bool<true> >
struct Set { typedef size_type type; };
template<typename T>
struct Set<T,Bool<Has_size_type<T>::value> > { typedef typename T::size_type type; };

Edit start: Here is another simpler approach:

template<typename T>
struct void_ { typedef void type; };

template<typename T, typename = void>
struct Set
{ typedef size_type type; };

template<typename T>
struct Set<T,typename void_<typename T::size_type>::type>
{ typedef typename T::size_type type; };

Edit end.

So finally, use as below:

template<typename some_container>
int func(some_container& input)
{
typedef typename Set<some_container>::type type;
}

So now type is either size_type or some_container::size_type, if it has that.

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();
};

C++ for-loop - size_type vs. size_t

The C++ Standard says,

 size_type  |  unsigned integral type  |  a type that can represent the size of the largest object in the
allocation model

Then it adds,

Implementations of containers
described in this International
Standard are permitted to assume that
their Allocator template parameter
meets the following two additional
requirements beyond those in Table 32.

  • The typedef members pointer, const_pointer, size_type, and
    difference_type are
    required to be T*,T const*, size_t, and ptrdiff_t, respectively

So most likely, size_type is a typedef of size_t.

And the Standard really defines it as,

template <class T> 
class allocator
{
public:
typedef size_t size_type;
//.......
};

So the most important points to be noted are :

  • size_type is unsigned integral, while int is not necessarily unsigned. :-)
  • it can represent the largest index, because it's unsigned.

Is std::container::size_type guaranteed to be size_t for standard containers with default allocator?

For STL-containers - nope. Table 96 of the standard in [container.requirements.general], which lists container requirements for any container X, explains it pretty clear:

Sample Image


However, for basic_string, size_type is defined as

typedef typename allocator_traits<Allocator>::size_type size_type;

which in turn will be size_t for std::allocator<..> as the allocator.

Also, std::array uses size_t as size_type, according to [array.overview]/3.

std::size_t or std::vector Foo ::size_type?

It is not necessarily true that std::vector<Foo>::size_type is the same as std::size_t. This is true even for C++11.

But personally I use std::size_t for a std::vector index irrespective of the type.

You could always use a static assertion if you're feeling particularly diligent. Obviously static_assert is a later addition beyond what's in C++98, but in that standard you could use something like

static char wrong_size_1[1 + sizeof(std::size_t) - sizeof(std::vector<Foo>::size_type)];

static char wrong_size_2[1 - sizeof(std::size_t) + sizeof(std::vector<Foo>::size_type)];

which would induce compile time failures if type types are not the same size.

std::size_t vs size_t vs std::string::size_type

Where does size_t come from when I don't have anything included in an empty project?

If you don't have anything included, then you can't use size_t. It's defined in <stddef.h> (and perhaps also in <cstddef>, if your version of that header puts the definitions in the global namespace as well as std).

Is it reasonable to always assume size_t == std::size_t?

Yes. All types and functions defined by the C library are included in the std namespace, as long as you include the appropriate C++ header (e.g. <cstddef> rather than <stddef.h>)

When should I use std::_::size_type?

Do you mean the size_type types defined in some standard classes and templates such as vector? You could use those when using those classes if you like. In most cases, you'll know that it's the same as size_t, so you might as well use that and save a bit of typing. If you're writing generic code, where you don't know what the class is, then it's better to use size_type in case it's not compatible with size_t.

For example, you might want to write a container designed to hold more items than can be represented by size_t. You might use some kind of big number type to represent the container's size, which isn't convertible to size_t. In that case, code like size_t s = c.size() would fail to compile - you'd need to use Container::size_type instead.

When should I use vector int ::size_type instead of size_t?

The primary time to use size_type is in a template. Although std::vector<T>::size_type is usually size_t, some_other_container<T>::size_type might be some other type instead1. One of the few things a user is allowed to add to the std namespace is a specialization of an existing template for some user defined type. Therefore, std::vector<T>::size_type for some oddball T could actually be some type other than size_t, even though the base template defined in the standard library probably always uses size_t.

Therefore, if you want to use the correct type for a specific container inside a template that works with that container, you want to use container::size_type instead of just assuming size_t.

Note, however, that generic code should rarely work directly with a container. Instead, it should typically work with iterators, so instead of container<T>::size_type, it would typically use something like std::iterator_traits<WhateverIterator>::difference_type instead.


  1. And for some specific T, vector<T>::size_type might be a different type as well--one of the few things you're allowed to put into the std namespace is a specialization of an existing class for a user-defined type, so for some T, vector<T> could use a completely different container than for most other types. This is typical for vector<bool>, but possible for other types as well.

Is `size_t` always an alias for `vector int ::size_type` or any other container type?

std::vector guarantees that pointers are valid iterators for its entire sequence, because vector::data returns "A pointer such that [data(), data() + size()) is a valid range." That's pointer addition, which is defined over std::ptrdiff_t, which is the signed version of std::size_t.

Also, [iterator.requirements.general]/6 applies:

… for integral values n and dereferenceable iterator values a and (a + n), *(a + n) is equivalent to *(addressof(*a) + n)

It's possible for vector::size_type to be narrower than std::size_t, for example 32 bits on a 64-bit system. But it's not something I'd worry about.



Related Topics



Leave a reply



Submit