I tried to set array size with const member variable like below
class A {
const int SIZE;
A() : SIZE(10) {}
void aaa() { int a[SIZE]; }
};
I cannot build
a[SIZE]
like this expression.
When I use GCC, build success. But when I use VC++ (Windows), I cannot build.
The error message is "'this' cannot be used in a constant expression"
How to set array size with const member variable?
Variable Length Arrays
when I use VC++ (Windows), I cannot build.
An array int a[SIZE] is created on stack with automatic storage duration.
Then this SIZE usually must be determined at compile-time in C++.
But your SIZE is defined without a value as follows:
const int SIZE;
Thus compilers can not know it's value at compile-time and would show compilation error.
So your SIZE is a dynamic variable.
When I use GCC, build success.
...But some C++ compilers support VLA (variable length arrays) which is a C99 addition and allows declaring C-style arrays on stack with a dynamic length.
VC++ does not support C99 and VLA, but GNU compiler supports VLA as an extension even in C90 and C++.
This is the reason why you can compile the above code by GCC without errors.
If you add -pedantic (-pedantic-errors) option to gcc compile command, we can get warnings (errors) for most gcc extensions.
In this case, with this option we should get warning (error) message: ISO C++ forbids variable length array 'a'.
How to fix the current error ?
(1) If you want to use C-style arrays, one way is making SIZE a macro as follows. Then compilers can know it's value at compile-time:
#define SIZE 10
class A
{
public:
A() {}
void aaa() { int a[SIZE]; }
};
(2) Defining the value of SIZE as a static const variable in the class definition, then compilers again can know it's value at compile-time:
class A
{
static constexpr int SIZE = 10;
public:
A() {}
void aaa() { int a[SIZE]; }
};
(3) C++ provides array functionality by std::vector and std::array which might make our code more readable, portable and robust.
I expect that std::array on stack would be more efficient than std::vector in your case, because 10 ints need less memory and std::array is allocated on stack just only once without causing problems.
This is a solution with std::array:
#include <array>
class A
{
static constexpr int SIZE = 10;
public:
A() {}
void aaa() { std::array<int, SIZE> a; }
};
Well, SIZE is probably not a macro because, here, you initialize it with 10 and have it as a variable. If it was a macro, your expression int a[SIZE] would work but you would not need lines 2-4 anyways.
If SIZE is, as here, a normal variable (which includes dynamically creating A with a size parameter, although the input might be from a macro), you should use a vector.
#include <vector>
class A {
const int SIZE;
A() : SIZE(10) {}
void aaa() { std::vector<int> a(SIZE); }
};
Usage of a is now the same.
If you are using an older compiler pre c++11, e.g c++03 you can also do enum {SIZE=10};
class A {
enum {SIZE=10};
void aaa() {int a[SIZE];}
};
Related
I have encountered the following problem.
Given we have class Foo, is there a way (trait/meta-program/trick/...) to determine(compile time) whether any of class members can do heap allocations?
Something like this:
class A
{
int a = 5;
std::array<int,10> arr;
...
};
class B
{
char c;
std::string s;
...
};
can_allocate<A> yields to false
can_allocate<B> yields to true
Potentially std::string can allocate if string surpasses small string optimization boundaries.
Is anyone aware of this kind of language feature, or some custom implementation? Or maybe its not even achievable?
This is not a complete answer, but if you can use c++20 and you can live with some restrictions (i.e. it'll work only for literal types you might be able leverage the fact that in c++20 memory allocations are allowed in constexpr context as long as they don't bubble into runtime code. So, maybe you're able to build on something along the following lines:
class A
{
int a = 5;
std::array<int,10> arr;
};
class B
{
char c{};
std::string s{};
};
template<typename T>
consteval T check_me() {
return {};
}
int main() {
check_me<A>();
check_me<B>(); // doesn't compile
}
Compiler Explorer.
It seems that msvc incorrectly does not refuse to compile this, but I think that's a compiler bug (?).
Also, note that this code only checks only the default constructor but not whether an allocation may happen in any member function of T or it's members.
The following class is meant to store a single bool and an array of 100 integers. I want a constructor that takes as parameters a single bool and a 100-element array (which are then copied into the object). The obvious way to write this is the following
class Info {
private:
const int contents[100];
const bool symmetric;
public:
Info (const int contents[100], bool symmetric) : contents(contents), symmetric(symmetric) {}
// ...
};
But unfortunately this does not compile, the contents argument in the member initialiser list (the third contentson that line) seems to be regarded as a pointer.
Can I get this to work or is this a C++ limitation?
I do not want to use std::array or std::vector for space and efficiency reasons. [edited] Mine is a C++ syntax question and not about whether std::array is efficient or not. The answer may be useful in related situations that do not allow the use of std::array. [end edited]
Initializing in the constructor body does not work, because contents is const.
I am using C++14 with clang (CLion in linux) and come from the Java world.
There is also another way other than std::array, but you should use templates and use variadic parameter feature.
Lets assume that the number of elements is amount then first declare your class like :
#include <utility>
template<size_t amount, typename = std::make_index_sequence<amount> >
class Info;
then define your class as the following:
template<size_t amount, size_t ... _Idx>
class Info<amount, std::integer_sequence<size_t, _Idx...> > {
private:
const int contents[amount];
const bool symmetric;
public:
Info (const int contents_[amount], bool symmetric_) : contents{contents_[_Idx]...}, symmetric(symmetric_) {}
};
now you use your class for any amount without any problem:
const int preinfo[3] = {1,2,3};
Info<3> minfo(preinfo, true);
This method will make it very fast but please note that nothing is free and using this code will make your code to produce a bigger binary after compilation!
I haven't worked with c++ in a while, but I just started a project with it. This may not be possible, but Im trying to create a template class with an array that sets its size to the value of a constant which i'm trying to set with the constructor.
This is the code of the constructor:
Tarray(int s): start_size(s){
}
This is the code that sets the array size:
const int start_size;
T this_array[start_size];
This is the entire file:
#ifndef TARRAY_H_
#define TARRAY_H_
template<typename T>
class Tarray {
private:
const int start_size;
T this_array[start_size];
int array_size;
public:
Tarray(int s): start_size(s){
}
~Tarray(){
delete[] this_array;
}
T & operator[](int i){
return this_array[i];
}
};
#endif /* TARRAY_H_ */
These are the errors I get:
..\/template_array/Tarray.h:16:24: error: 'Tarray<T>::start_size' cannot appear in a constant-expression
..\/template_array/Tarray.h:16:34: error: 'new' cannot appear in a constant-expression
..\/template_array/Tarray.h:16:34: error: ISO C++ forbids initialization of member 'this_array' [-fpermissive]
..\/template_array/Tarray.h:16:34: error: making 'this_array' static [-fpermissive]
..\/template_array/Tarray.h: In instantiation of 'Tarray<Person>':
..\Human.cpp:17:24: instantiated from here
..\/template_array/Tarray.h:16:34: error: invalid in-class initialization of static data member of non-integral type 'Person*'
Build error occurred, build is stopped
Time consumed: 343 ms.
The error messages have been changing as I try to tweak the code, but these are the errors from this particular build.
Thanks for any help
The reason you're getting compiler errors is this line:
T this_array[start_size];
This line would make your Tarray actually contain start_size instances of T. It wouldn't hold a pointer or reference to these instances - they would be part of same block of memory that contains the Tarray's other instance variables.
This would make the class' size depend on start_size, and start_size is not known at compile time. The size of any C++ class must be known at compile time, this isn't possible.
There are two ways to solve this:
Allocate the array of T instances on the heap, using array new. This is what std::vector does. Writing such a class and getting it to behave right when it's copied/moved/expanded/etc is difficult and tedious, so I'd recommend just using std::vector instead.
Make the number of T instances fixed, and pass it as a template parameter
i.e.:
template<typename T, std::size_t N>
class TArray
{
...
T this_array[N];
...
}
This is what std::array (C++11 only) and boost::array do. Again, I'd recommend using one of these instead of writing your own. Unless this is homework, of course...
Lastly, it's worth noting that this is an error:
~Tarray(){
delete[] this_array;
}
this_array wasn't allocated with new, so you shouldn't delete it. If the array is part of the class as it is here (rather than being separately heap-allocated and owned by the class), then it will be destroyed along with the rest of the class by default. Calling delete is not only unnecessary, it will almost certainly cause a crash.
std::vector is precisely the tool for this job:
template<typename T>
class Tarray {
private:
std::vector<T> this_array;
public:
Tarray(int s): this_array(s){
}
~Tarray(){
}
T & operator[](int i){
return this_array[i];
}
};
The following code does something similar but not using the constructor:
#ifndef TARRAY_H_
#define TARRAY_H_
template<int SizeT>
class Tarray {
private:
T this_array[SizeT];
public:
Tarray() {}
~Tarray() {}
T & operator[](int i){
return this_array[i];
}
};
#endif /* TARRAY_H_ */
and you can use it like this:
TArray<10> myArray;
You have to create the array at run time.
template<typename T>
class Tarray {
private:
const int start_size;
T* this_array;
int array_size;
Tarray( const Tarrat& inObj ); // no copy
public:
Tarray(int s): start_size(s), this_array( new T[s] ) {
}
~Tarray(){
delete[] this_array;
}
T & operator[](int i){
return this_array[i];
}
};
Note, for this to work, T must have a default constructor (that is, a constructor that takes no arguments).
Use std::vector instead, and make life simple for yourself. :)
(If you want a fixed-size array, then std::array might be a possibility, I think that's in C++11, if not, then boost probably has an implementation).
If you insist on having that ordinary array syntax, though, as if you were using ye-olde C, then you will need to use a template parameter, such that your template class has two arguments - one for the 'T' it already has now, and another for the array size.
You are making life especially difficult by managing that array yourself - if you feel you have to define a destructor, you really should define the copy constructor in addition to the constructor. (That's called the Rule Of The Big Three, if I recall correctly), instead, rely on RAII and avoid having to ever explicitly call operator delete or delete[] yourself.
I've found a strange looking piece of code in a project I have to maintain. There's an empty array member of a class which doesn't lead to an compiler error. I've tested some variations of such a code with MSVC 10.0:
template<class T> struct A {
int i[];
}; // warning C4200: nonstandard extension used : zero-sized array in struct/union
template<class T> struct B { static int i[]; };
template<class T> int B<T>::i[];
struct C {
int i[];
}; //warning C4200: nonstandard extension used : zero-sized array in struct/union
template<class T> struct D { static int i[]; };
template<class T> int D<T>::i[4];
template<> int D<int>::i[] = { 1 };
int main()
{
A<void> a;
B<void> b;
C c;
D<void> d0;
D<int> d1;
a.i[0] = 0; // warning C4739: reference to variable 'a' exceeds its storage space
b.i[0] = 0; // warning C4789: destination of memory copy is too small
c.i[0] = 0; // warning C4739: reference to variable 'c' exceeds its storage space
int i[]; // error C2133: 'i' : unknown size
d0.i[0] = 0; // ok
d0.i[1] = 0; // ok
return 0;
}
The error message at int i[] is absolutely sensible to me. The code which is shown with class D is well-formed standard C++. But what's about the classes A, B and C? What kind of types are the member variables int i[] in this classes?
EDIT:
your doubt is explained by the definition of the extension to the language, which allows for zero-sized arrays at the end of structs/unions. I have not tried it, but if you declare another member after the zero-sized array, it should fail.
so, if you allocate a variable on the stack, you have to know its size; the exception to the rule is when allocating an array at the end of a struct/union, where some C-typical trickery is possible.
In c++ this raises a warning because the default copy constructor and assignment operator will probably not work.
PREVIOUS ANSWER:
The compiler warns you about the fact that you are trying to define an array with zero size. This is not allowed in standard C/C++.
Let's see the differences class by class.
In class D:
template<class T> struct D { static int i[]; };
it works because you are just declaring the type of a static member variable. For this to link, you need also defining the actual array, in a definition statement like you do:
template<> int D<int>::i[] = { 1 };
here you also specify the size of the array through the initializer.
With class B, you are doing something similar, but the definition is:
template<class T> int B<T>::i[];
i.e., you don't specify the size and get the warning.
With class A, more of the same, you are defining a member variable of type array without the size.
Good one. Just to be certain, you are wondering why the compiler isn't flagging it as an error right? In that case, I think this problem is unpredictable across compilers but I'm aware of this happening on MSVC all the time.
http://support.microsoft.com/kb/98409
Let me see if I can explain it like they did. If I were to declare a struct with an empty array like this,
struct a
{
int x;
char empty[];
};
the compiler might allocate 4 bytes for x and probably another 4 bytes for the char pointer. empty will contain the address 4 bytes past the start of struct a.
Since it is a character array of no length, trying to access it would be an error since there is no trailing 0 to signify the end of the string.
I could choose to initialize the struct later to point to the start of an actual string to overcome this error.
struct a myStruct = { 1, "hello world"}; // empty now points to the start of "hello world"
Since a struct is basically a class, turns out you can do the same thing with a class if you make sure its an aggregate and not a full class.
So there ya go. MSVC compilers treat arrays with no fixed sized as a pointer when declared within a struct/class. Remember that class definitions are merely just declarations. The compiler doesn't allocate space for them until you create an instance for it. When you start to think about it, it sorta makes since. How will the compiler know if you plan to allocate storage for it later. It becomes a run-time artifact but the compiler was still smart enough to warn you about the problem.
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?
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;
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_;
}
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.
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).