4

I'd like to implement a simple native C++ fixed-capacity array template class, supporting the range-based "for each" syntax for convenience, with minimal overhead.

I'm having trouble with supporting it on const instances.

With this implementation:

template< class T, size_t Capacity >
class List
{
public:
    List() { mSize = 0; }

    const T* begin() const { return mItems; }
    const T* end() const { return mItems + mSize; }

    T* begin() { return mItems; }
    T* end() { return mItems + mSize; }

private:
    size_t mSize;
    T mItems[ Capacity ];
};

and this usage:

const List< int, 5 > myInts;
for each( const int myInt in myInts )
{
    continue;
}

I get this error:

error C2440: 'initializing' : cannot convert from 'const int *' to 'int *'
    Conversion loses qualifiers

This usage doesn't complain:

List< int, 5 > myInts;
for each( const int myInt in myInts )
{
    continue;
}

And this (undesirable) implementation doesn't complain:

template< class T, size_t Capacity >
class List
{
public:
    List() { mSize = 0; }

    T* begin() const { return const_cast< List* >( this )->mItems; }
    T* end() const { return const_cast< List* >( this )->mItems + mSize; }

private:
    size_t mSize;
    T mItems[ Capacity ];
};

What is going on under the hood that I'm not understanding? What is it about std::vector<> that handles this properly? Thanks!

beau
  • 113
  • 7

1 Answers1

2

Your use-case seems a bit strange for me as there is no for each constructs in C++ as you have written it down. There is a regular for and range-based for introduced in C++11. I can only guess what your real use case is, but most likely compiler complains due to const-correctness mistake. I cannot really pin down your mistake exactly without a real code that you are trying to run. At any rate, below is a working example demonstrating both usages. Hope it is helpful, but if you have any questions — feel free to follow up, I will try to explain.

#include <cstdlib>
#include <iostream>

template <typename T, std::size_t Capacity>
class List {
  public:
    List() : mSize(0) {}

    const T *begin() const { return mItems; }
    const T *end() const { return mItems + mSize; }

    T *begin() { return mItems; }
    T *end() { return mItems + mSize; }

    void add(int v)
    {
        // TODO: Check for out of range here...
        mItems[mSize++] = v;
    }

  private:
    size_t mSize;
    T      mItems[Capacity];
};

int main()
{
    /* const */ List<int, 10> array;

    array.add(1);
    array.add(11);
    array.add(15);
    array.add(3);

    // C++11 style (range-based for)
    for (int p : array) {
        std::cout << p << '\n';
    }

    // Pre C++11 style
    for (const int *from = array.begin(), *to = array.end(); from != to; ++from)
    {
        int p = *from;
        std::cout << p << '\n';
    }
}
  • Thanks for your time Vlad. I failed to mention that "for each" seems to be syntactic sugar specific to recent Microsoft compilers (I'm not concerned with portability). If VS2010 supports the C++11 syntax you describe, I'm happy to use it - I'm not at a dev machine now, but I assume for( const int p : array ) would work just as well? Still, I'm curious why "for each" isn't working for me, and can't seem to find any good documentation that might shed some light. – beau Dec 10 '12 at 05:35
  • @beau Oh, I've googled it, seems like non-standard microsoft specific construct introduced in VS 2005. I can't tell much about it since I have not used MS compiler for years... But VS 2010 definitely supports standard C++11 range based for, you might have to select it in project settings though. Sorry for being useless here, I've tried ;) –  Dec 10 '12 at 05:43
  • 1
    @beau: C++11 range-based `for` loops were only added in VS 2012. – Blastfurnace Dec 10 '12 at 05:46
  • 1
    @Blastfurnace: Nerts. So my original question has not lost any importance (to me). – beau Dec 10 '12 at 06:16