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
isunsigned
integral, whileint
is not necessarilyunsigned
. :-)- 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:
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.
- 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 thestd
namespace is a specialization of an existing class for a user-defined type, so for someT
,vector<T>
could use a completely different container than for most other types. This is typical forvector<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 valuesa
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
Why Is 'This' a Pointer and Not a Reference
C/C++ Check If One Bit Is Set In, I.E. Int Variable
What Are the Pointer-To-Member Operators -≫* and .* in C++
Why Can't I Have a Non-Integral Static Const Member in a Class
What Is More Efficient? Using Pow to Square or Just Multiply It With Itself
Initializing Fields in Constructor - Initializer List VS Constructor Body
How to Construct a Std::String With an Embedded Null
C/C++ Include Header File Order
Why Do We Not Have a Virtual Constructor in C++
View Array in Visual Studio Debugger
C++: Const Reference, Before VS After Type-Specifier
A Free Tool to Check C/C++ Source Code Against a Set of Coding Standards
Why Can Lambdas Be Better Optimized by the Compiler Than Plain Functions
Removing Leading and Trailing Spaces from a String
How to Set Up Google C++ Testing Framework (Gtest) With Visual Studio 2005