2 years ago I did this:
SmallVector - std::vector like container on the stack
now, 2 years later, I am reinventing the wheel again, this time with fully constexpr SmallVector.
This time, because I decided it will have only constexpr functions, I decided to remove all non POD types.
#ifndef MY_SMALL_VECTOR_H_
#define MY_SMALL_VECTOR_H_
#include <stdexcept> // std::bad_alloc while push_back
#include <type_traits> // std::is_trivial, std::is_standard_layout
#include <initializer_list>
//
// Based on
// http://codereview.stackexchange.com/questions/123402/c-vector-the-basics
// http://lokiastari.com/posts/Vector-SimpleOptimizations
//
template<typename T, std::size_t SIZE>
class SmallVector{
static_assert(std::is_trivial<T>::value, "T must be POD...");
static_assert(std::is_standard_layout<T>::value, "T must be POD...");
private:
static constexpr std::size_t SIZEOF = sizeof(T);
static constexpr bool DEBUG_ = true;
public:
// TYPES
using value_type = T;
using size_type = std::size_t;
using iterator = T*;
using const_iterator = const T*;
private:
size_type length = 0;
T buffer[SIZE] = {};
public:
// STANDARD C-TORS
constexpr SmallVector() = default;
template<class IT>
constexpr SmallVector(IT begin, IT end){
appendCopy(begin, end);
}
constexpr SmallVector(const std::initializer_list<T> & list) :
SmallVector(list.begin(), list.end()){}
// MISC
constexpr
void reserve(size_type const) const noexcept{
// left for compatibility
}
constexpr
void clear() noexcept{
length = 0;
}
// COMPARISSON
constexpr bool operator==(const SmallVector &other) const noexcept{
if (length != other.length)
return false;
auto first = other.begin();
auto last = other.end();
auto me = begin();
for(; first != last; ++first, ++me){
if ( ! (*first == *me) )
return false;
}
return true;
}
template<typename CONTAINER>
constexpr bool operator!=(const CONTAINER &other) const noexcept{
return ! operator==(other);
}
// ITERATORS
constexpr
iterator begin() noexcept{
return buffer;
}
constexpr
iterator end() noexcept{
return buffer + length;
}
// CONST ITERATORS
constexpr const_iterator begin() const noexcept{
return buffer;
}
constexpr const_iterator end() const noexcept{
return buffer + length;
}
// C++11 CONST ITERATORS
constexpr const_iterator cbegin() const noexcept{
return begin();
}
constexpr const_iterator cend() const noexcept{
return end();
}
// SIZE
constexpr size_type size() const noexcept{
return length;
}
constexpr bool empty() const noexcept{
return size() == 0;
}
// MORE SIZE
constexpr size_type capacity() const noexcept{
return SIZE;
}
constexpr size_type max_size() const noexcept{
return SIZE;
}
// DATA
constexpr
value_type *data() noexcept{
return buffer;
}
constexpr const value_type *data() const noexcept{
return buffer;
}
// ACCESS WITH RANGE CHECK
constexpr
value_type &at(size_type const index){
validateIndex_(index);
return buffer[index];
}
constexpr const value_type &at(size_type const index) const{
validateIndex_(index);
return buffer[index];
}
// ACCESS DIRECTLY
constexpr
value_type &operator[](size_type const index) noexcept{
// see [1] behavior is undefined
return buffer[index];
}
constexpr const value_type &operator[](size_type const index) const noexcept{
// see [1] behavior is undefined
return buffer[index];
}
// FRONT
constexpr
value_type &front() noexcept{
// see [1] behavior is undefined
return buffer[0];
}
constexpr const value_type &front() const noexcept{
// see [1] behavior is undefined
return buffer[0];
}
// BACK
constexpr
value_type &back() noexcept{
// see [1] behavior is undefined
return buffer[length - 1];
}
constexpr const value_type &back() const noexcept{
// see [1] behavior is undefined
return buffer[length - 1];
}
// MUTATIONS
constexpr
void push_back(const value_type &value){
emplace_back(value);
}
constexpr
void push_back(value_type &&value){
emplace_back(std::move(value));
}
template<typename... Args>
constexpr
void emplace_back(Args&&... args){
if (length == SIZE){
throw std::bad_alloc{};
}
buffer[length++] = value_type(std::forward<Args>(args)...);
}
constexpr
void pop_back() noexcept{
// see [1]
--length;
}
public:
// NON STANDARD APPEND
template<class IT>
constexpr
void appendCopy(IT begin, IT end) {
for(auto it = begin; it != end; ++it)
push_back(*it);
}
private:
constexpr
void validateIndex_(size_type const index) const{
if (index >= length){
throw std::out_of_range("Out of Range");
}
}
// Remark [1]
//
// If the container is not empty,
// the function never throws exceptions (no-throw guarantee).
// Otherwise, it causes undefined behavior.
};
#endif
Here is small demo / test:
#include "smallvector.h"
using vectorPod = SmallVector<int, 5>;
namespace{
constexpr auto test_constexpr2(){
vectorPod v{};
v.push_back(4);
v.clear();
v.emplace_back(5);
v.push_back(54);
int a = 9;
v.push_back(a);
vectorPod w{ 5, 54, 9 };
if (v == w)
v.at(0) = 7;
auto x = v.begin();
++x;
*x = 4;
return v;
}
} // namespace
int main(){
constexpr auto v = test_constexpr2();
return v[0];
}
union Buffer { T elem; char dummy; }; Buffer elems[N]; std::size_t count;
to support constexpr non-POD types. \$\endgroup\$