Ali
Ali

Reputation: 58471

How to deduce array size from an enum template argument?

How should I change the code below so that Array<Index> array; is enough and the SIZE is automatically deduced from the enum?
Even if the enum changes, it is guaranteed that it contains SIZE referring to the correct size.

template <typename Enum, int N>
class Array {

public:
    int& operator[](Enum index) { return array[index]; }

private:
    int array[N];
};

enum Index { X, Y, SIZE };

int main() {

    Array<Index, SIZE> array;

    array[X] = 1;

    return 0;
}

UPDATE: As for "Array<type> means you're creating an array of Type objects" (Jerry) and "the name of class template is a bit misleading" (Nawaz): actually I am creating CustomSqlQueryModel<TableColumns>. The above is just a simplified code, nothing more. Jerry and Nawaz are rigth: this simplified code is unfortunate.

Upvotes: 7

Views: 2287

Answers (3)

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361482

If you want only the size to be template argument, not the type , as from your example it seems that the type of the array would be always int, then why don't you implement this:

template <int size>
class Array {

public:
    int& operator[](int index) { return array[index]; }

    //Note this addition!
    int operator[](int index) const { return array[index]; }
private:
    int array[size];
};

int main() {

    Array<10> array;

    array[0] = 1;
    array[1] = 2;

    return 0;
}

Note this addition: it's better if you implement const version of operator[] too, so that const Array<> can use it to access the array elements, otherwise your class wouldn't work for const Array<>.

Upvotes: 0

Fred Nurk
Fred Nurk

Reputation: 14212

You can write a traits class. This requires a bit of extra work each time you define a new enum type, but no extra work for each occurrence of Array<Index> in user code:

template<class Enum>
struct ArrayTraits;

template<class Enum>
struct Array {
  int& operator[](Enum index) { return array[index]; }

private:
  int array[ArrayTraits<Enum>::size];
};

enum Index { X, Y, SIZE };

template<>
struct ArrayTraits<Index> {
  enum { size = SIZE };
};


int main() {
  Array<Index> array;
  array[X] = 1;
  return 0;
}

One of the advantages of this is you can specialize the traits for external enums you don't control, as long as you know how to get the max size.

Upvotes: 6

Jerry Coffin
Jerry Coffin

Reputation: 490178

As stated, I don't think you can. If, however, you change it to something like:

struct Index { 
    enum { X, Y, SIZE};
};

Then your template could be something like:

template <class Enum>
class Array { 
// ...

private:
    int array[Enum::SIZE];
};

...and if the type you pass as Enum doesn't include some positive constant named SIZE,the instantiation won't compile. For the purpose at hand, you'd really kind of prefer that Index was a namespace, but since a namespace isn't a type, I don't think you can use it as a template argument.

I should add, however, that I'm not sure I like this idea at all -- most people are going to think Array<type> means you're creating an array of Type objects, and this is clearly something entirely different from that...

Upvotes: 1

Related Questions