size of array is not an integral constant-expression

J

johnehein

#include <vector>
using namespace std;

template <typename Iter>
int
foo(Iter first, Iter last, int nn)
{
const size_t n = last - first;
double buf[n];
return 0;
}

int
main(int argc, char **argv)
{
vector<double> x;
foo(x.begin(), x.end(), argc);
return 0;
}

foo.cc:17: instantiated from here
foo.cc:9: error: size of array is not an integral constant-expression

g++ 4.2.1

Is this error specific to g++ 4.x? g++ 3.6.4 and g++ 2.9.5 have no
problems with it, but that doesn't mean they are right. Is there some
reason to expect this to fail.

There are a few interesting workarounds that point to this being
unexpected behavior... I'll post those next.
 
I

Ian Collins

#include <vector>
using namespace std;

template <typename Iter>
int
foo(Iter first, Iter last, int nn)
{
const size_t n = last - first;
double buf[n];
return 0;
}

int
main(int argc, char **argv)
{
vector<double> x;
foo(x.begin(), x.end(), argc);
return 0;
}

foo.cc:17: instantiated from here
foo.cc:9: error: size of array is not an integral constant-expression

g++ 4.2.1

Is this error specific to g++ 4.x?
No, n is not a compile time constant, the error specific to any
conforming compiler!

The expression

const size_t n = last - first;

double buf[n];

declares (in C99) a variable length array, something gcc in default mode
has supported for years.

C++ does not have VLAs.
 
V

Victor Bazarov

#include <vector>
using namespace std;

template <typename Iter>
int
foo(Iter first, Iter last, int nn)
{
const size_t n = last - first;
double buf[n];
return 0;
}

int
main(int argc, char **argv)
{
vector<double> x;
foo(x.begin(), x.end(), argc);
return 0;
}

foo.cc:17: instantiated from here
foo.cc:9: error: size of array is not an integral constant-expression

g++ 4.2.1

Is this error specific to g++ 4.x?

Not that I can see. 'last - first' is a run-time expression. And when
you use it to initialise a 'const size_t', the variable ('n') also
becomes a run-time expression. It cannot be used to declare an array.
g++ 3.6.4 and g++ 2.9.5 have no
problems with it, but that doesn't mean they are right. Is there some
reason to expect this to fail.

Yes, there is. It goes against the rules of the language. The older
versions of G++ may have had it as an extension. Hell, the new versions
may still have it as an extension, and you're welcome to use it, just
don't claim your program to be C++.
There are a few interesting workarounds that point to this being
unexpected behavior... I'll post those next.

What's so unexpected in actually implementing the rules of the language?

V
 
V

Vidar Hasfjord

On Mar 12, 8:09 pm, (e-mail address removed) wrote:
[...]
 const size_t n = last - first;
 double buf[n];

In C++ this is trying to instantiate an array type at run-time; which
is not legal. Think of "double buf [n]" as the instantiation of the
parameterized type "T [n]" (essentially "array <T, n>"). Since "n" is
not a compile-time constant it cannot be passed as an argument to the
parameterized type.

Regards,
Vidar Hasfjord
 
J

johnehein

I'm not sure why everyone is giving the big thumbs down to run time
array sizing. C++ doesn't have a problem with the concept in
general. As mentioned by someone, C99 has some support for it, too,
but that's not directly relevant to the issue I've just noticed.

How is the example any different the following code (other than the
obvious differences)...

template <typename Iter>
int
foo(Iter first, Iter last, int nn)
{
const size_t n = last - first;
double buf[nn];
return 0;

}

int
main(int argc, char **argv)
{
vector<double> x;
foo(x.begin(), x.end(), argc);
return 0;

}


The only change is that it takes a run-time int that is passed in to
size the array rather than a iterator subtraction.


Even more interesting, if I remove the 'const' for n, it compiles
fine. Here are some of the examples of similar ways to do the same
thing...

Case #1 is basically what is failing (with or without the cast)
#2-#5 are all workarounds
2) remove const
3) remove const with a twist
4) typedef instead of template
5) use new

1) slight variation on original just for thoroughness... cast iter
math to size_t... no go.
=====
template <typename Iter>
int
foo(Iter first, Iter last, int nn)
{
const size_t n = (size_t)(last - first);
double buf[n]; <----- this is line 12
return buf - buf;
}

foo.cc:12: error: size of array is not an integral constant-expression


2) remove const from n... COMPILES!
=====
template <typename Iter>
int
foo(Iter first, Iter last, int nn)
{
size_t n = (last - first);
double buf[n];
return buf - buf;
}


3) use intermediate const xx with non-const n... COMPILES!
=====
template <typename Iter>
int
foo(Iter first, Iter last, int nn)
{
size_t n = (last - first);
const size_t xx = n;
double buf[xx];
return buf - buf;
}


4) use typedef instead of template (regardless of constness)...
COMPILES!
=====
typedef vector<double>::const_iterator Iter;
int
foo(Iter first, Iter last, int nn)
{
const size_t n = (last - first);
double buf[n];
return buf - buf;
}


5) use const, template, but use new to allocate... COMPILES!
=====
template <typename Iter>
int
foo(Iter first, Iter last, int nn)
{
const size_t n = last - first;
double *buf = new double[n];
return buf - buf;
}


So there seems to only be one case where I get get dynamic sizing of
the array to fail.
 
J

johnehein

So, it seems that adding -pedantic gives:


foo.cc:10: error: ISO C++ forbids variable-size array 'buf'


Sorry for the noise. Somewhere along the way I had gotten the
impression that the c++ standard included support for variable length
arrays. Apparently not - just a gcc-ism.
 
I

Ian Collins

I'm not sure why everyone is giving the big thumbs down to run time
array sizing. C++ doesn't have a problem with the concept in
general. As mentioned by someone, C99 has some support for it, too,
but that's not directly relevant to the issue I've just noticed.
It isn't part of the language.

All the things you have noted are specific gcc behaviour when that
compiler isn't used in compliant mode.

If you use gcc, use

g++ -ansi -pedantic -Wall
 
J

James Kanze

What's so unexpected in actually implementing the rules of the
language?

The fact that almost no compiler does? How many support export,
for example? If I base my expectations on practical experience,
I expect that any given compiler is likely to deviate from the
rules in various places.

It makes writing truely portable code very hard.
 
J

James Kanze

I'm not sure why everyone is giving the big thumbs down to run
time array sizing.

I haven't seen anyone saying that they were against it. Only
that it is illegal in C++.
C++ doesn't have a problem with the concept in general.

C++ requires the dimensions of a C style array to be a integral
constant expression whenever you define an array. There are no
exceptions.
As mentioned by someone, C99 has some support for it, too, but
that's not directly relevant to the issue I've just noticed.

For whatever reasons, the C++ standards committee decided not to
adapt this feature of C99.
How is the example any different the following code (other
than the obvious differences)...
template <typename Iter>
int
foo(Iter first, Iter last, int nn)
{
const size_t n = last - first;
double buf[nn];
return 0;
}
int
main(int argc, char **argv)
{
vector<double> x;
foo(x.begin(), x.end(), argc);
return 0;
}
The only change is that it takes a run-time int that is passed
in to size the array rather than a iterator subtraction.

And? It's also illegal. It doesn't compile with a conforming
compiler. (Sun CC rejects it, as does g++ when you request full
standards conformance: -std=c++98 -pedantic.)

[...]
So there seems to only be one case where I get get dynamic
sizing of the array to fail.

I get it systematically. And the language standard requires a
diagnostic.
 
D

Default User

James said:
On Mar 13, 12:45 am, (e-mail address removed) wrote:

For whatever reasons, the C++ standards committee decided not to
adapt this feature of C99.

They probably felt std::vector was a better solution.





Brian
 
R

Ron Natalie

Default said:
They probably felt std::vector was a better solution.
It was pretty essential to C lacking <vector> but it's a
half assed lame solution there as are almost everything to
do with arrays in C and C++.

It wasn't even well developed in the C standards process
by the time the C++ standard was being finalized, so it's
hard to blame them for not trying to second guess how the
C standards guys were going to kludge it up.
 
J

James Kanze

It was pretty essential to C lacking <vector> but it's a
half assed lame solution there as are almost everything to
do with arrays in C and C++.
It wasn't even well developed in the C standards process by
the time the C++ standard was being finalized, so it's hard to
blame them for not trying to second guess how the C standards
guys were going to kludge it up.

It's pretty well developed in the C standard today, but hasn't
been adopted into C++-0x. I think the reason (or part of the
reason) is that the entire proposal (VLAs) contains a bit more,
some of it has serious repercussions with regards to class
types, which would require some adaptation for C++, and no one
felt motivated enough to work out the C++ wording. But I'm just
guessing.
 
V

Vidar Hasfjord

[C99 VLAs]
It's pretty well developed in the C standard today, but hasn't
been adopted into C++-0x.

I'm inclined to think that this is a good thing. I guess the main
motivator for adopting it is the elusive ideal of harmonizing C and C+
+. I don't think it is worthwhile in this case. While I can see the
benefits of the semantics of VLAs the chosen syntax is an awful
overload of static arrays, while the type of the thing will not have
anything in common with the type of a static array. VLAs and arrays
are two very different things.

In my view a better solution for dynamic stack allocation in C++ would
be a lower-level feature; as support for a standard library class. The
ideal would be a general "allocation overload" mechanism for
constructors, so that you can differentiate between construction on
the stack and on the heap. Then some mechanism for allocating stack
space would be used by the "stack constructor". This would allow any
library type, e.g. std::vector, to be optimized for stack allocation.

An idea would be for the language to automatically pass an extra
parameter to the constructor if an object is constructed on the stack.
This parameter has a built-in type, say std::auto_allocator, which
allows allocation of stack space in the parent frame; i.e. the stack
frame in which the construction takes place. For example,

vector (size_t n, std::auto_allocator& a) : buf (a.alloc (n *
sizeof T)) {...}

Aside: Many C++ implementations provide a stack allocation feature as
an extension (alloca). Unfortunately, alloca cannot be used to create
a data type that mimics a VLA since it allocates memory in the stack
frame of the caller; i.e. you will have to call it explicitly; it
cannot be encapsulated.

Regards,
Vidar Hasfjord
 
I

Ian Collins

Jeff said:
What is the purpose?

Much the same as VLAs, to save the cost of a dynamic allocation.
alloca shares the biggest drawback of VLAs - lack of failure notification.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top