views:

87

answers:

4

I want to initialize constant in child-class, instead of base class. And use it to get rid of dynamic memory allocation (I know array sizes already, and there will be a few child-classes with different constants).
So I try:

class A {
public:
    const int x;

    A() : x(0) {}
    A(int x) : x(x) {}
    void f() {
        double y[this->x];
    }
};

class B : A {
    B() : A(2) {}
};

Pretty simple, but compiler says:

error C2057: expected constant expression

How can I say to compiler, that it is really a constant?

+4  A: 

It isn't a constant though. It can still be modified by the constructor. Only a compile time constant is allowed for the size of an array. When the compiler says "constant expression", it is not meaning an expression which returns a constant value, but an constant, such as "52" or "45" or something along those lines.

Use std::vector instead.

EDIT: In response to "I know array sizes already, and there will be a few child-classes with different constants"

The only way to do that is to use a template.

template<size_t x>
class A {
public:
    void f() {
        double y[x];
    }
};

typedef A<2> B;
Billy ONeal
+2  A: 

The behaviour you expect could be achieved using the following template.

Note that this is actually unreliable, disgusting and could be used only as "a sample". Use std::vector instead.

template <size_t a = 0>
class A {
public:
   A() { }

   void f() {
      int y[a];
      y[0] = 5;
   }
};

class B : A<2> {
   B() { }
};

void main() {
   A<1> a;
   a.f();

   // Undefined behaviour - creating an array of size 0
   // At least, MSVS2008 treats it as an error :)
   // A<0> a_;
}
Kotti
Gah; beaten my four seconds!
strager
Not sure it's such a good idea to allow negative values. (Use an unsigned integral type like size_t, not int)
Billy ONeal
How is `// Error here!` causing an error? If anything, it's just undefined behaviour at `y[0] = 5;`.
strager
`const` is redundant in a template specifier -- there's really no reason for it to be there. And you can use a simple typedef to get class B rather than creating an inheritance tree.
Billy ONeal
@stranger It's a compile-time error because you can't have an array of size 0.
Kotti
@Kotti: It's not that you can't, it's that it's undefined behavior. Some compilers will throw errors, some will not. If no error is thrown, that does not make the code valid.
Billy ONeal
+1 for changing your answer to use `size_t` :)
Billy ONeal
@Billy Thanks for pointing that `array[0]` is actually an undefined behaviour.
Kotti
+1  A: 

There's "constant", and then there's "constant". If you want to allocate an array on the stack like that, the compiler needs the length of the array at compile time, and based on what you've given there it can't figure that out. Interestingly, gcc supports an extension (not supported in standard C++) that allows for stack allocation for variable lengths.

Reinderien
The reason GCC supports this is it's a C99 feature not duplicated by C++. C++0x did not add the feature because C++ has `vector`, making the utility of the variable length array nill.
Billy ONeal
+1  A: 

I don't know if it will work for your purposes, but one possibility would be to make it a template parameter:

template <int size>
class A { 
    double y[size];
};

In this case, you'd probably want to create an instance of A in B instead of using inheritance.

The other obvious possibility would be to use a tr1::array object instead. This is is also a template, so the idea is pretty much the same, but it's already written, tested and working so you can avoid all that. If your compiler doesn't supply TR1 classes, Boost has a mostly conforming implementation (boost::array).

Jerry Coffin