an array reference cannot appear in a constant-expression

J

Juha Nieminen

Assume something like this:

namespace
{
const int array[] = { 1, 2, 3 };

struct A
{
static const int value = array[0];
};
}

This gives the error "an array reference cannot appear in a
constant-expression".

My question: Why not? What would be the technical problem? You can
certainly use const ints in constant-expressions, so why can't elements
of a const array be used?

This is a hindrance. I had a situation where I had such a const array
of integral values, and it would have been useful to get the sum of
those values at compile time. Template metaprogramming could be used to
iterate over the array and sum the values... except that the elements of
an array cannot be used in compile-time constant expressions. Rather
annoying.
 
P

Pascal J. Bourguignon

Juha Nieminen said:
Assume something like this:

namespace
{
const int array[] = { 1, 2, 3 };

This doesn't define a constant array, but an array of constant integers.

struct A
{
static const int value = array[0];
};
}

This gives the error "an array reference cannot appear in a
constant-expression".

My question: Why not? What would be the technical problem? You can
certainly use const ints in constant-expressions, so why can't elements
of a const array be used?

I think the problem is that the order of initialization of the const
and static stuff is not determined.

First, static variables are initialized before normal variables, so
you'd need at very least declare array to be static, but even in that
case, the order is not specified.

This is a hindrance. I had a situation where I had such a const array
of integral values, and it would have been useful to get the sum of
those values at compile time. Template metaprogramming could be used to
iterate over the array and sum the values... except that the elements of
an array cannot be used in compile-time constant expressions. Rather
annoying.

Yes, if you don't want to be annoyed when doing meta-programming you
should use Common Lisp.
 
J

Juha Nieminen

Pascal said:
Juha Nieminen said:
Assume something like this:

namespace
{
const int array[] = { 1, 2, 3 };

This doesn't define a constant array, but an array of constant integers.

Arrays are, by definition, const. That's why you can do this:

struct A
{
static const int elements = sizeof(array)/sizeof(array[0]);
};

The compiler will resolve the size of the array at compile-time just
fine. It just won't resolve its elements, even if they are const. I
can't think of a technical reason for this.

Compare to this, which is just fine:

namespace
{
const int value1 = 1, value2 = 2, value3 = 3;

struct A
{
static const int value = value1; // ok
}
}
I think the problem is that the order of initialization of the const
and static stuff is not determined.

const ints are not "initialized" in any order at runtime. They have a
compile-time value. So do the elements of an array containing const ints.

How would an array of const ints be initialized, if you think about
it? That's right: From an array of compile-time ints. The compiler knows
what values the array will have, at compile time. I can't think of any
reason why accessing them at compile time would be forbidden.
Yes, if you don't want to be annoyed when doing meta-programming you
should use Common Lisp.

Trolling isn't really an answer.
 
M

Michael Doubez

Juha Nieminen said:
  Assume something like this:
namespace
{
    const int array[] = { 1, 2, 3 };

This doesn't define a constant array, but an array of constant integers.

You cannot modify array all the same.

    struct A
    {
        static const int value = array[0];
    };
}
  This gives the error "an array reference cannot appear in a
constant-expression".

The short answer is "the standard says so".
I don't have the standard here here but googling your problem yields:
"An integral constant-expression can involve only literals (2.13),
enumerators, const variables or static data members of integral or
enumeration types initialized with constant expressions (8.5)..."

I think the problem is that the order of initialization of the const
and static stuff is not determined.

They are in the same compilation unit, there is no problem regarding
order of initialisation.

It is a constraint by the language. A wild guess is that it would
requires a const array *and* a const index and nobody went through the
trouble of specifying it.
First, static variables are initialized before normal variables, so
you'd need at very least declare array to be static, but even in that
case, the order is not specified.


Yes, if you don't want to be annoyed when doing meta-programming you
should use Common Lisp.

Or not use meta-programming at all.

A solution is to use a typelist of integer dependent type (like
vector_c in Boost.MPL).

But it is not for the faint of heart and probably not worth the
trouble.
 
M

Michael Doubez

Juha Nieminen said:
  Assume something like this:
namespace
{
    const int array[] = { 1, 2, 3 };
This doesn't define a constant array, but an array of constant integers..

You cannot modify array all the same.
    struct A
    {
        static const int value = array[0];
    };
}
  This gives the error "an array reference cannot appear in a
constant-expression".
[snip]
A solution is to use a typelist of integer dependent type (like
vector_c in Boost.MPL).

But it is not for the faint of heart and probably not worth the
trouble.

Looking into Boost documentation, the answer is not that hard.

Something like:

typedef vector_c<int,1,2,3> array;
typedef accumulate< array, int_<0>, plus<_1,_2> >::type array_sum;

const int sum=array_sum::value;

Alternatively, you could use a runtime expression that could be
optimized out by the compiler:

// compute the sums of elements ptr/index
template <class T,int index>
struct sum_ptr
{
static T op(const T* array) {
return sum_ptr<T,index-1>::eek:p(array) + array[index] ;
}
};

// stop at index 0
template <class T>
struct sum_ptr<T,0>
{
static T op(const T* array) {
return array[0];
}
};

template <class T,int N>
T sum(const T (&array)[N])
{
return sum_ptr<T,N-1>::eek:p(array);
}

And then
const int array[] = { 1, 2, 3 };
const int array_sum=sum(array);

Not tested but should compile
 
K

kwikius

A solution is to use a typelist of integer dependent type (like
vector_c in Boost.MPL).

But it is not for the faint of heart and probably not worth the
trouble.

Yep. repeating all the mistakes of the STL in a compile time library
version clearly wasnt too bright an idea :)

regards
Andy Little
 
M

Michael Doubez

Michael said:
Pascal J. Bourguignon  wrote:
Juha Nieminen writes:
Assume something like this:
namespace
{
        const int array[] = { 1, 2, 3 };
This doesn't define a constant array, but an array of constant integers.
You cannot modify array all the same.

Below x is clearly not a const object, even though you can't modify any of
its members.

    struct X { const int i, j; };
    X x = { 0, 0 };

The powers that be have decreed that 8.3.4:
[...]Any type of the form “cv-qualifier-seq array of N T” is adjusted
to “array of N cv-qualifier-seq T,”

This means that a const array *is* an array of const element. Which is
not true of your exemple.
 
J

James Kanze

Assume something like this:
namespace
{
const int array[] = { 1, 2, 3 };
struct A
{
static const int value = array[0];
};
}
This gives the error "an array reference cannot appear in a
constant-expression".
My question: Why not?

The simple answer is because arrays are hopelessly broken in C,
and C++ just keeps them for reasons of C compatibility. (But
of course, there are functionalities of C style arrays which
aren't subsumed by std::vector. The possibility of purely
static initialization and automatic determination of the size,
for example.)
What would be the technical problem?

The technical problem is that the [] operator isn't array
indexing, but simply syntactic sugar for *(a+i). And there are
relatively valid technical reasons why dereferencing (even of a
constant expression) isn't allowed in constant expressions.
You can certainly use const ints in constant-expressions, so
why can't elements of a const array be used?

Because you can't access them without dereferencing a pointer.
 
J

James Kanze

Juha Nieminen said:
Assume something like this:
namespace
{
const int array[] = { 1, 2, 3 };
This doesn't define a constant array, but an array of constant
integers.

In this case, that's actually what is wanted. Juha doesn't
really care if the array is constant, as long as the accessed
element is constant.
struct A
{
static const int value = array[0];
};
}
This gives the error "an array reference cannot appear in a
constant-expression".
My question: Why not? What would be the technical problem?
You can certainly use const ints in constant-expressions, so
why can't elements of a const array be used?
I think the problem is that the order of initialization of the
const and static stuff is not determined.

I don't think this is an issue here. IIUC, Juha is asking why
the expression "array[0]" isn't an integral constant expression
(a compile time constant), in the sense of the standard. And
the reason is that the standard bans "functions, class objects,
pointers or references" in integral constant expressions, and
"array[0]" supposes a conversion of array to a pointer.
First, static variables are initialized before normal
variables, so you'd need at very least declare array to be
static, but even in that case, the order is not specified.

You're confusing several things (easy to do, since they are all
designated by the same word: static). The keyword static is
irrelevant here---if there was no const involved, it would
affect linkage (but not lifetime or initialization); with the
const, the object is "implicitly" static, by default.[1] All
objects defined at namespace scope have static lifetime. And
objects with static lifetime whose initializers are all constant
expressions have static initialization, provided their
constructor is "trivial".

[1] This is, at least, the interpretation of all of the
implementations I've tried it on. IMHO, the standard actually
says that his declaration should have external linkage without
the static: as you pointed out to begin with, the array (the
object being defined) isn't const, so the text in the second
bullet of §3.5/2 doesn't apply.
 
M

Michael Doubez

Assume something like this:
namespace
{
    const int array[] = { 1, 2, 3 };
    struct A
    {
        static const int value = array[0];
    };
}
This gives the error "an array reference cannot appear in a
constant-expression".
My question: Why not?

The simple answer is because arrays are hopelessly broken in C,
and C++ just keeps them for reasons of C compatibility.  (But
of course, there are functionalities of C style arrays which
aren't subsumed by std::vector.  The possibility of purely
static initialization and automatic determination of the size,
for example.)

But in this case, the type is well known int ()[3] and as not decayed
to pointer type. Every information is known about 'arrray', how would
it be considered broken here .

What would be the technical problem?

The technical problem is that the [] operator isn't array
indexing, but simply syntactic sugar for *(a+i).  And there are
relatively valid technical reasons why dereferencing (even of a
constant expression) isn't allowed in constant expressions.

Without deferencing the value, the compiler can looks up the name and
retrieve the value (at least for array of POD).
Because you can't access them without dereferencing a pointer.

Will it still be true in the next standard with constexpr ?
 
J

James Kanze

Pascal said:
Assume something like this:
namespace
{
const int array[] = { 1, 2, 3 };
This doesn't define a constant array, but an array of
constant integers.
Arrays are, by definition, const. That's why you can do this:

An array cannot be const, by proclamation in the standard. (At
least I think this is the case. I can't find the text in the
C++ standand, but in C, it clearly says "If the specification of
an array type includes any type qualifiers, the element type is
so qualified, not the array type."

Note that there is no syntax for directly qualifying an
array---the const in your example applies to "int", and not to
"int[]". You can define "const arrays" by means of a typedef,
however; in this case, at least in C, the const automatically
"moves down" to the elements.
 
J

James Kanze

On 27 mai, 15:07, (e-mail address removed) (Pascal J. Bourguignon)
wrote:
Juha Nieminen said:
Assume something like this:
namespace
{
const int array[] = { 1, 2, 3 };
This doesn't define a constant array, but an array of
constant integers.
You cannot modify array all the same.

According to the C standard (and I think this applies to C++ as
well), you can never modify an array. That doesn't make it
const, however; the type is still non-const. The const in the
above applies to the array's elements, which are const, and
cannot be modified.

All of which is horrible language lawyer type stuff. In
practice, you can think of both the elements and the array as
being const, and probably not miss anything.

[...]
It is a constraint by the language. A wild guess is that it
would requires a const array *and* a const index and nobody
went through the trouble of specifying it.

The fact that arrays can't be const (formally) may have
something to do with it, but the fundamental reason, I think, is
that accessing an array element involves dereferencing a
pointer, and nobody apparently wanted to go to the effort of
specifying when that could be done in an integral constant
expression. (Note that an expression like a+i is a constant
expression of pointer type if a is a constant pointer, and i is
a constant integer. But in general, this expression is
actually evaluated by the linker, not the compiler.)
 
J

James Kanze

Michael said:
Pascal J. Bourguignon wrote:
Juha Nieminen writes:
Assume something like this:
namespace
{
const int array[] = { 1, 2, 3 };
This doesn't define a constant array, but an array of
constant integers.
You cannot modify array all the same.
Below x is clearly not a const object, even though you can't
modify any of its members.
struct X { const int i, j; };
X x = { 0, 0 };
The powers that be have decreed that 8.3.4:
[...]Any type of the form “cv-qualifier-seq array of N T” is adjusted
to “array of N cv-qualifier-seq T,”

That's the passage I was looking for.
This means that a const array *is* an array of const element.

This means that there is no such thing as a const array. If
you try to declare an array const, it is not the array which is
const, but the individual elements.
Which is not true of your exemple.

I think his example was just to point out that there are
non-const objects which still cannot be modified.

The standard uses the term "modifiable lvalue" in several places
(and in one place "nonmodifiable lvalue"). I couldn't find a
definition of the term in the C++ standard, but in the C
standard: "A modifiable lvalue is an lvalue that does not have
array type, does not have an incomplete type, does not have a
const-qualified type, and if it is a structure or union, does
not have any member (including, recursively, any member or
element of all contained aggregates or unions) with a
const-qualified type."

In sum: objects of array type are never const, but never
modifiable. (The same doesn't apply to elements of the array,
of course. The elements may be const or not, and if they are
not const, they can be modified.)
 
M

Michael Doubez

Michael Doubez wrote:
Pascal J. Bourguignon  wrote:
Juha Nieminen writes:
Assume something like this:
namespace
{
        const int array[] = { 1, 2, 3 };
This doesn't define a constant array, but an array of
constant integers.
You cannot modify array all the same.
Below x is clearly not a const object, even though you can't
modify any of its members.
    struct X { const int i, j; };
    X x = { 0, 0 };
The powers that be have decreed that 8.3.4:
[...]Any type of the form “cv-qualifier-seq array of N T” is adjusted
to “array of N cv-qualifier-seq T,”

That's the passage I was looking for.
This means that a const array *is* an array of const element.

This means that there is no such thing as a const array.  If
you try to declare an array const, it is not the array which is
const, but the individual elements.

I agree that array-type cannot be const-qualified but they are
immutable all the same.
I think his example was just to point out that there are
non-const objects which still cannot be modified.

I understand but the difference I make is that struct are not
immutable by nature.

And struct can declare member mutable which add to the mess since I
can have a const struct but be able to modify it.

In the OP question, whether a array is const or not is irrelevant, IMO
the compiler could know the value without a heavy boilerplate (at
least for numeric or even POD types).

[snip]
 
K

kwikius

On May 27, 10:30 pm, Michael Doubez <[email protected]> wrote:
In sum: objects of array type are never const, but never
modifiable.  (The same doesn't apply to elements of the array,
of course.  The elements may be const or not, and if they are
not const, they can be modified.)

Ideally in designing a programming language IMO any object that isnt
qualified should be const by default so if you want a mutable object
you have to do extra work. This is i think the practise in functional
languages. If I review my code there are a very large proportion of
cases where objects are never modified after they have been
initialised, but it is tedious in C++ to add the const modifier
everywhere though doing so greatly helps to understand the big picture
of what the code is doing. however ideally non-constness should be
what jumps out rather than constness.

regards
Andy Little
 
P

Pascal J. Bourguignon

kwikius said:
Ideally in designing a programming language IMO any object that isnt
qualified should be const by default so if you want a mutable object
you have to do extra work. This is i think the practise in functional
languages. If I review my code there are a very large proportion of
cases where objects are never modified after they have been
initialised, but it is tedious in C++ to add the const modifier
everywhere though doing so greatly helps to understand the big picture
of what the code is doing. however ideally non-constness should be
what jumps out rather than constness.

Integer x=42;
try{
x=12;
}catch(CannotMutateANonMutableObject& e){
}
assert(x==42);
Mutable<Integer> y=42;
y=12; // ok
assert(x==12);

Implementation of Integer and Mutable<C> left as an exercise for the
programmer ;-)
 
K

kwikius

On May 28, 1:55 pm, (e-mail address removed) (Pascal J. Bourguignon)
Implementation of Integer and Mutable<C> left as an exercise for the
programmer ;-)

not sure it needs to be that sophisticated as shown below and even
that little bit is pleasingly descriptive, but its up to you to impose
the discipline by using the typedefs and thats not the same as it
being the compiler enforced semantics. The C++ semantic is wrong... we
live n learn... its as simple as that.

#include <type_traits> // in gcc4.3 vc++9
#include <iostream>

typedef const int int_;

#define VAR(T) std::tr1::remove_const< T >::type

int main()
{
int_ x = 1;
VAR(int_) y = 2;
//x=y; // error
y=x;

}


regards
Andy Little
 

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,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top