Does someone know the way to define constant-sized vector?
For example, instead of defining
std::vector<int>
it will be
std::vector<10, int>
It should be completely cross-platformed. Maybe an open source class?
Does someone know the way to define constant-sized vector?
For example, instead of defining
std::vector<int>
it will be
std::vector<10, int>
It should be completely cross-platformed. Maybe an open source class?
The std::vector can always grow dynamically, but there are two ways you can allocate an initial size:
This allocates initial size and fills the elements with zeroes:
std::vector<int> v(10);
v.size(); //returns 10
This allocates an initial size but does not populate the array with zeroes:
std::vector<int> v;
v.reserve(10);
v.size(); //returns 0
There is no way to define a constant size vector. If you know the size at compile time, you could use C++11's std::array aggregate.
#include <array>
std::array<int, 10> a;
If you don't have the relevant C++11 support, you could use the TR1 version:
#include <tr1/array>
std::tr1::array<int, 10> a;
or boost::array, as has been suggested in other answers.
This is an old question but if someone just needs constant-size indexed container with size defined at runtime, I like to use unique_ptr:
// c++14
auto constantContainer = std::make_unique<YourType []> ( size );
// c++11
std::unique_ptr<YourType[]> constantContainer {new YourType[ size ]};
// Access
constantContainer[ i ]
If you want a fixed compile-time specified size (ala std::array<T, N>
), but you want to be able to populate the vector with varying numbers of elements between 0
and N
, then a good option is eastl::fixed_vector
.
std::vector:
The size of a std::vector
is dynamic - it will allocate required storage dynamically, and you cannot limit the size and enforce an error.
You can however reserve
a certain size, and then add elements up that size before it needs to allocate new storage.
vector.size()
is initially 0, and increases as you add elementss
std::array:
The size of a std::array
is a compile-time constant - it will allocate required storage statically, and you cannot change the size.
array.size()
is always the size of the array, and is equal to array.max_size()
eastl::fixed_vector:
The size of an eastl::fixed_vector
can be either static or dynamic.
It will allocate a certain number of elements initially, and then if you allow dynamic growth, will allocate dynamically if required.
For the purpose you originally asked for, you can disable growth (via bEnableOverflow
in the template instantiation below)
fixed_vector.size()
is initially 0, and increases as you add elements.
template<typename T,
size_t nodeCount,
bool bEnableOverflow = true,
typename OverflowAllocator =
typename eastl::type_select<bEnableOverflow,
EASTLAllocatorType,
EASTLDummyAllocatorType>::type>
class fixed_vector;
Simple example:
#include <iostream>
#include <vector>
#include <array>
#include "EASTL/fixed_vector.h"
int main()
{
std::vector<int> v;
v.reserve(10);
std::cout << "size=" << v.size() << " capacity=" << v.capacity() << '\n';
std::array<int, 10> a;
std::cout << "size=" << a.size() << " capacity=" << a.max_size() << '\n';
eastl::fixed_vector<int, 10, false> fv;
std::cout << "size=" << fv.size() << " capacity=" << fv.capacity() << '\n';
return 0;
}
Output:
size=0 capacity=10
size=10 capacity=10
size=0 capacity=10
Note that the size of array
is 10, whereas vector
and fixed_vector
are 0
Use std::array c++11
For better readability you can make typedef:
typedef std::array<int, 10> MyIntArray;
A std::vector
is a dynamic container, there is no mechanism to restrict its growth. To allocate an initial size:
std::vector<int> v(10);
C++11 has a std::array
that would be more appropriate:
std::array<int, 10> my_array;
If your compiler does not support C++11 consider using boost::array
:
boost::array<int, 10> my_array;
This ----> std::vector<10, int>
is invalid and causes error. But the new C++ standard has introduced a new class; the std::array. You can declare an array like this:
std::array<int, 5> arr; // declares a new array that holds 5 ints
std::array<int, 5> arr2(arr); // arr2 is equal to arr
std::array<int, 5> arr3 = {1, 2, 3, 4, 5}; // arr3 holds 1, 2, 3, 4, 5
The std::array
has constant size and supports iterator/const_iterator/reverse_iterator/const_reverse_iterator
. You can find more about this class at http://cplusplus.com/reference/stl/array/.
Improving the answer from Pari
I have been using the following class for many years in number crunching applications:
inline void nodeleter(void*) {}
/// Array of T with ownership. Like \see std::unique_ptr<T[]> but with size tracking.
/// @tparam T Element type.
template <typename T>
class unique_array : public std::unique_ptr<T[],void (*)(void*)>
{ size_t Size;
private:
typedef std::unique_ptr<T[],void (*)(void*)> base;
protected:
unique_array(T* ptr, size_t size, void (*deleter)(void*)) noexcept : base(ptr, deleter), Size(size) {}
void reset(T* ptr, size_t size) noexcept { base::reset(ptr); Size = size; }
public:
constexpr unique_array() noexcept : base(nullptr, operator delete[]), Size(0) {}
explicit unique_array(size_t size) : base(new T[size], operator delete[]), Size(size) {}
template <size_t N> unique_array(T(&arr)[N]) : base(arr, &nodeleter), Size(N) {}
unique_array(unique_array<T>&& r) : base(move(r)), Size(r.Size) { r.Size = 0; }
void reset(size_t size = 0) { base::reset(size ? new T[size] : nullptr); Size = size; }
void swap(unique_array<T>&& other) noexcept { base::swap(other); std::swap(Size, other.Size); }
void assign(const unique_array<T>& r) const { assert(Size == r.Size); std::copy(r.begin(), r.end(), begin()); }
const unique_array<T>& operator =(const unique_array<T>& r) const { assign(r); return *this; }
size_t size() const noexcept { return Size; }
T* begin() const noexcept { return base::get(); }
T* end() const noexcept { return begin() + Size; }
T& operator[](size_t i) const { assert(i < Size); return base::operator[](i); }
unique_array<T> slice(size_t start, size_t count) const noexcept
{ assert(start + count <= Size); return unique_array<T>(begin() + start, count, &nodeleter); }
};
reset
, but as the name suggests this discards any data.const
instances do not make their content const
. Only the storage cannot be changed.Container
requirement, first of all the typedefs. I never needed them so far.std::is_arithmetic<std::complex<double>>::value
is false.I found that std::vector
actually has max_size()
member and there is a customization point in the std library to tune vector behavior accordingly. So I just tuned std::vector
to apply a cap on max_size()
and narrow size_type
.
Compiles with c++11 and newer
Most of the code is meaningless boilerplate you can easily cut off if you don't need to support cross platform compilation and/or older versions of c++, the only real logic is inside of Allocator
members. Use any allocator and vector types that are compatible with the standard and you can have a caped vector. using CapedVector = Caped<uint16_t>::Vector<char>;
You can drop Vector
and just use template <class T> using CapedVector = std::vector<T, Caped<uint16_t>::Allocator<std::allocator<T>>>
if you don't need to narrow size_type
#include <algorithm>
#include <limits>
#include <memory>
#include <vector>
#include <type_traits>
#if __cpp_constexpr >= 201603L
// c++17
#define NODISCARD [[nodiscard]]
#else
#define NODISCARD
#endif
#if __cpp_constexpr > 200704L
// c++14
#define CONSTEXPR constexpr
#else
#define CONSTEXPR
#endif
#ifndef __cpp_concepts
template <class Size, Size maxSize = std::numeric_limits<Size>::max(), class = typename std::enable_if<std::is_unsigned<Size>::value>::type>
#elif defined CONSTEXPR
template <class Size, Size maxSize = std::numeric_limits<Size>::max(), class = std::enable_if_t<std::is_unsigned_v<Size>>>
#else
template <class Size, Size maxSize = std::numeric_limits<Size>::max()> requires(std::is_unsigned_v<Size>)
#endif
struct Caped
{
using size_type = Size;
template <class A>
struct Allocator : A
{
using value_type = typename std::allocator_traits<A>::value_type;
using pointer = typename std::allocator_traits<A>::pointer;
using const_pointer = typename std::allocator_traits<A>::const_pointer;
using void_pointer = typename std::allocator_traits<A>::void_pointer;
using const_void_pointer = typename std::allocator_traits<A>::const_void_pointer;
using difference_type = typename std::allocator_traits<A>::difference_type;
using propagate_on_container_copy_assignment = typename std::allocator_traits<A>::propagate_on_container_copy_assignment;
using propagate_on_container_move_assignment = typename std::allocator_traits<A>::propagate_on_container_move_assignment;
using propagate_on_container_swap = typename std::allocator_traits<A>::propagate_on_container_swap;
#ifdef __cpp_lib_allocator_traits_is_always_equal
using is_always_equal = typename std::allocator_traits<A>::is_always_equal;
#endif
using A::A;
using size_type = Caped::size_type;
template <class U>
struct rebind
{
using other = Allocator<typename std::allocator_traits<A>::template rebind_alloc<U>>;
};
CONSTEXPR size_type max_size() const noexcept
{
using BaseSize = typename std::allocator_traits<A>::size_type;
static_assert(sizeof(BaseSize) >= sizeof(size_type), "allocator size_type must be bigger than cap type");
return static_cast<size_type>(std::min<BaseSize>(maxSize, std::allocator_traits<A>::max_size(*this)));
}
NODISCARD CONSTEXPR pointer allocate(std::size_t n)
{
return n <= max_size() ? std::allocator_traits<A>::allocate(*this, n) : throw std::bad_array_new_length();
}
#ifdef __cpp_lib_allocate_at_least
NODISCARD constexpr pointer allocate_at_least(std::size_t n)
{
return n <= max_size() ? std::allocator_traits<A>::allocate_at_least(*this, n) : throw std::bad_array_new_length();
}
#endif
};
template<class T, template <class, class> class V = std::vector, class A = std::allocator<T>>
struct Vector : V<T, Allocator<A>>
{
using Base = V<T, Allocator<A>>;
using typename Base::value_type;
using typename Base::allocator_type;
using typename Base::difference_type;
using typename Base::reference;
using typename Base::const_reference;
using typename Base::pointer;
using typename Base::const_pointer;
using typename Base::iterator;
using typename Base::const_iterator;
using typename Base::reverse_iterator;
using typename Base::const_reverse_iterator;
using Base::Base;
using Base::assign;
using Base::insert;
using size_type = typename allocator_type::size_type;
CONSTEXPR Vector(size_type count, const T& value, const allocator_type& alloc = allocator_type()) : Base(count, value, alloc){}
#if _MSVC_LANG <= 201402L
CONSTEXPR explicit Vector(size_type count) : Base(count) {};
CONSTEXPR explicit Vector(size_type count, const allocator_type& alloc) : Base(count, alloc){}
#else
CONSTEXPR explicit Vector(size_type count, const allocator_type& alloc = allocator_type()) : V(count, alloc){}
#endif
CONSTEXPR void assign(size_type count, const T& value) { Base::assign(count, value); }
CONSTEXPR reference at(size_type pos) { return Base::at(pos); }
CONSTEXPR const_reference at(size_type pos) const { return Base::at(pos); }
CONSTEXPR reference operator[](size_type pos) { return Base::operator [](pos); };
CONSTEXPR const_reference operator[](size_type pos) const { return Base::operator [](pos); };
CONSTEXPR size_type size() const noexcept { return static_cast<size_type>(Base::size()); }
CONSTEXPR size_type max_size() const noexcept { return static_cast<size_type>(Base::max_size()); }
CONSTEXPR size_type capacity() const noexcept { return static_cast<size_type>(Base::capacity()); }
CONSTEXPR iterator insert(const_iterator pos, size_type count, const T& value) { return Base::insert(pos, count, value); };
CONSTEXPR void resize(size_type count) { Base::resize(count); };
CONSTEXPR void resize(size_type count, const value_type& value) { Base::resize(count, value); };
};
};