views:

833

answers:

3

I need an array where size is known at compile time. I know I can use std::vector or boost::array. But that's doesn't teach me how it works internally. Also I couldn't find how to add items into boost::array other than using the initializer. I have written the following code for a generic array. My intention is to get familiar with iterators, template specializations etc. Following is the code

template<typename T>
struct iterator_traits
{
    typedef T           value_type;
    typedef T&          reference_type;
    typedef T*          iterator;
    typedef const T*    const_iterator;
    typedef std::reverse_iterator<iterator> reverse_iterator;
};

template<typename T>
struct iterator_traits<T*>
{
    typedef T*          value_type;
    typedef T*&         reference_type;
    typedef T**         iterator;
    typedef const T     const_iterator;
    typedef std::reverse_iterator<iterator> reverse_iterator;
};

template<typename T, size_t size = 10>
class Array
{
    public:

        typedef typename iterator_traits<T>::value_type       value_type;
        typedef typename iterator_traits<T>::reference_type   reference_type;
        typedef typename iterator_traits<T>::iterator         iterator;
        typedef typename iterator_traits<T>::const_iterator   const_iterator;
        typedef typename iterator_traits<T>::reverse_iterator reverse_iterator;

        Array() : lastIndex(0) {
        }

        void add(value_type element) {
            if(lastIndex >= size)
                throw std::out_of_range("Array is full");
            array_[lastIndex++] = element;
        }

        reference_type at(unsigned int index){
            if(index < size)
                return array_[index];
            else
                throw std::out_of_range("Invalid index");
        }

        size_t capacity(){
            return size;
        }

        iterator begin(){
            return array_;
        }

        iterator end(){
            return array_ + size;
        }

        const_iterator begin() const{
            return array_;
        }

        const_iterator end() const{
            return array_ + size;
        }

        reverse_iterator rbegin() {
            return reverse_iterator(end());
        }

        reverse_iterator rend() {
            return reverse_iterator(begin());
        }

    private:

        value_type array_[size];
        unsigned int lastIndex;
};

The above code works well. Following are my questions

1 - How can I create my array like boost::array does? Something like

Array<int> ints = { 10, 12 };

2 - Are there any pitfalls in the code?

3 - I had to use a specialization for pointer types in traits. Is that the best practice?

4 - Iterator pattern is implemented correctly or not?

Any thoughts would be great!

+2  A: 

boost::array uses a feature of the language: A struct with no constructors can use an initialization list. If you provide your own constructor, you can't use an initialization list.

Also, you're using iterator_traits wrong. Your code should be something like

    // type definitions
    typedef T              value_type;
    typedef T*             iterator;
    typedef const T*       const_iterator;
    typedef T&             reference;
    typedef const T&       const_reference;
    typedef std::size_t    size_type;
    typedef std::ptrdiff_t difference_type;

iterator_traits is for traits of iterators. Also, you can just use a pointer as an iterator. The STL explicitly allows this.

rlbond
I tried this. But it will not work when type is a pointer. This is the reason why I created iterator traits.
Appu
Look at boost/array.hpp. It does work.
rlbond
+1  A: 

2 - are there any pitfalls ?

I would get rid of the default size "size = 10". What makes 10 the default size of an array ? What I see happening instead is someone accidentally leaving off the size, and assuming that its bigger than it is.

Regarding adding elements and the C-style initialization, I don't believe its possible to do both. It works in boost because (I believe) the object is, quite simply, an array under the covers. It can't dynamically re-size. The class simply adds iterators (and other sugar like ::at()) to a plain array.

keraba
+2  A: 

1 - How can I create my array like boost::array does? Something like

Array<int> ints = { 10, 12 };

In c++, you can only (currently) use a braces enclosed initializer list if your struct, union or c-style array meets the criteria of being an aggregate. To do such, according to the standard:

8.5.1.1 An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

You would have to make your class have those features if you wanted to use them in the current standard. The next standard (see here) includes a provision to allow other types to do so.

2 - Are there any pitfalls in the code?

Here is one: the reason you can't add things to a boost list is it always has the same number of elements (the size it was allocated with). In your array you can add elements, but you've still constructed 10 elements underneath the hood during construction. this could lead to some suprising results if the user isn't expecting the default constructor called 10 times.

Todd Gardner