Mawg
Mawg

Reputation: 40145

Can I limit the size of a C++14 Vector?

As the tile says, is there anyway that I can declare a vector in C++ 14 and limit the max number of entries it can hold?

Upvotes: 6

Views: 188

Answers (1)

Ted Lyngmo
Ted Lyngmo

Reputation: 117937

One possible solution would be to create your own Allocator. std::allocator is stateless, but yours could implement the max_size() member function which should return the value of a member variable that carries the max number of elements.

An example that will work from C++11 up until at least C++20:

template<class T>
struct limited_allocator : public std::allocator<T> {
    using value_type = typename std::allocator<T>::value_type;
    using size_type = typename std::allocator<T>::size_type;
    using difference_type = std::ptrdiff_t;
    using propagate_on_container_move_assignment = std::true_type;

    using is_always_equal = std::false_type; // not needed since C++23

#if __cplusplus < 201703L
    using pointer = typename std::allocator<T>::pointer;
    using const_pointer = typename std::allocator<T>::const_pointer;
    using reference = typename std::allocator<T>::reference;
    using const_reference = typename std::allocator<T>::const_reference;

    template<class U> struct rebind {
        typedef limited_allocator<U> other;
    };
#endif
    
    // No default constructor - it needs a limit:
    constexpr limited_allocator(size_type max_elements) noexcept :
        m_max_elements(max_elements) {}

    constexpr limited_allocator( const limited_allocator& other ) noexcept = default;

    template< class U >
    constexpr limited_allocator( const limited_allocator<U>& other ) noexcept :
        m_max_elements(other.m_max_elements) {}

    // Implementing this is what enforces the limit:
    size_type max_size() const noexcept { return m_max_elements; }

private:
    size_type m_max_elements;
};

Since this allocator isn't stateless, you'd better implement the non-member comparison functions too:

template< class T1, class T2 >
constexpr bool operator==(const limited_allocator<T1>& lhs,
                          const limited_allocator<T2>& rhs ) noexcept {
    return &lhs == &rhs;
}

template< class T1, class T2 >
constexpr bool operator!=(const limited_allocator<T1>& lhs,
                          const limited_allocator<T2>& rhs ) noexcept {
    return &lhs != &rhs;
}

A usage example, in which the vector is allowed to keep 1 element only:

int main() {
    std::vector<int, limited_allocator<int>> vec(limited_allocator<int>(1));
//                   ^^^^^^^^^^^^^^^^^^^^^^      ^^^^^^^^^^^^^^^^^^^^^^^^^
    try {
        vec.push_back(1);   // one element
        vec.pop_back();     // zero again
        vec.push_back(2);   // one again
        vec.push_back(3);   // here it'll throw
    }
    catch(const std::length_error& ex) {
        std::cout << "length_error: " << ex.what() << '\n';
    }    
    catch(const std::bad_array_new_length& ex) {
        std::cout << "bad_array_new_length: " << ex.what() << '\n';
    }
    catch(const std::bad_alloc& ex) {
        std::cout << "bad_alloc: " << ex.what() << '\n';
    }
}

Possible output:

length_error: vector::_M_realloc_insert

Demo

Upvotes: 8

Related Questions