Engineering a minimalist image interface without templates I'm practicing different ways of class design, so I'd like feedback on the design characteristics. Of course code practice comments are also welcome.

It was a conscious decision to omit templates. My next version will use a class template to store image data, similar to CImg and boost::GIL. I intend to make more classes with increasing complexity (templates, abstract base class, variadic templates). Until I reach a detailed yet highly abstract image class.

Header:

#ifndef AIMG
#define AIMG

#include &lt;vector&gt;
#include &lt;string&gt;

class AImg
{
public:
  const std::vector&lt;short&gt; dimsizes; //or std::array of length 4?
  const int N;     //# of elements ( = product of dimension sizes)
  unsigned int Bytes {1}; //bytes per element
  std::string tag; //meta info like "rgb"
  std::string cast;//used for returning data of type cast
  void* data;      //pointer to some array/container/byte-blob

  AImg(const std::vector&lt;short&gt;&amp;); //you have to specify image size when creating. But may be empty of data.
  void setdata(void*);
  void* getdata();

  //example of pseudo-template method:
  void plusone(std::string);
  int operator()(unsigned int); //variable args would allow better indexing

private:
  int getN();
  //checkcast() ?
};

//public fcns
AImg::AImg(const std::vector&lt;short&gt;&amp; input): dimsizes(input), N(getN()), cast("") {}
int AImg::operator()(unsigned int idx){
   if (cast=="int")
    {return ((int*)data)[idx];} else return 0;
  }
void  AImg::setdata(void* input){data=(void*)input;}
void* AImg::getdata(){return data;}
void  AImg::plusone(std::string cast){
        if (cast=="int")
        {
          int* idata = (int*)data;
          for (int i = 0;i&lt;N;i++){idata[i] += 1;}
        }
        else if (cast == "float")
        {
          float* fdata = (float*)data;
          for (int i = 0;i&lt;N;i++){fdata[i] += 1.0;}
        }
        //...other types
        // careful: N is based on elements of some user-defined type. Not all types have the same precision (#bytes).
        // So you'd need to normalize N based on the type size ( sizeof() ) to properly iterate over the data, or forbid certain types
      }

//Private fcns
int AImg::getN(){ //doesn't STL have a std::prod() ?!?!
  int prod = 1;
  for(short n : dimsizes) prod *= n;
  return prod;
}

#endif // AIMG


Main:

#include &lt;iostream&gt;
#include "AImg.h"

using std::cout;  using std::cin;
using std::endl;  using std::vector;

int main()
{
  //create image object of size 2x2
AImg img(vector&lt;short&gt; {2,2});
  //create image data: simple int array
int d[4] {1,2,3,4};
img.setdata(d); //make sure length of d equals N, i.e. fill img up completely
img.plusone("int");
int* d2 = (int*)img.getdata(); //need to explicitly re-cast in c++
cout&lt;&lt;d2[0]&lt;&lt;d2[1]&lt;&lt;d2[2]&lt;&lt;d2[3]&lt;&lt;endl;
img.cast = "int";
cout&lt;&lt;img(0)&lt;&lt;img(1)&lt;&lt;img(2)&lt;&lt;img(3)&lt;&lt;endl;
}


Personally I think a class design like this is risky to use, so should I add more safety checks? It also seems harder to maintain and extend compared to a class using templates.

It also seems hard to extend the model to allow for inhomogenous (sub)pixels. For example, the value of a pixel at (1,1,1) could be an int but the value of the pixel at (1,1,2) could be an unsigned char.
A few notes about things and stuff (I know, my introductions are always original):


About that comment:

//doesn't STL have a std::prod() ?!?!


The standard library does have a function to compute the accumulated product of a range. It's called std::accumulate. You can use it like this:

int n = std::accumulate(
    std::begin(dimsizes), std::end(dimsizes),
    1, std::multiplies&lt;int&gt;{}
);


Agreed, it's a bit verbose. But the function is quite generic since it may work with any well-formed binary operation. Note that if you are using C++14, you can use std::multiplies&lt;&gt; instead of std::multiplies&lt;int&gt; and let the type be deduced.
Also, you mentioned std::array. If you know for sure that you will only store 4 values and not more, then please, by all means, use an std::array&lt;short, 4&gt; instead of an std::vector&lt;short&gt;.
Your case isn't really consistent. For local variables, the generally accepted practice is to use snake_case everywhere and to drop every capital letter.
Ok, I hate to say this, but void AImg::plusone(std::string cast) without templates really hurts me. I know that you plan to change that later, but the current version still hurts me ç___ç Also, since you're not altering the std::string, it would be better to take a const std::string&amp; parameter instead.
Instead of that:

int AImg::operator()(unsigned int idx);


Array subcript tends to use the... well, the array subcript operator[] instead of the parenthesis. Unless you have to feed it several parameters, in which case operator() is sometimes preferred, but that's not your case.

int AImg::operator[](unsigned int idx);

I don't think that the method setdata really needs a cast from void* to void*:

void AImg::setdata(void* input) { data = input; }