Re: Am i just blind? Is "array = array;" allowed?
On Aug 27, 5:17 pm, Juha Nieminen <nos...@thanks.invalid> wrote:
Goran Pusic <gor...@cse-semaphore.com> wrote:
On Aug 27, 4:02 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:
I really can't find where the Standard forbids the following!
int a[2] = { 1, 2 };
int b[2];
b = a;
I thought somewhere the Standard says that lvalue
expressions of array type are nonmodifiable, but I can't
find it!
No idea about what standard might think, but if it did think
something, it would still sound like a silly arbitrary
constraint. What would be the rationale to prevent said
assignment?
There is none. Whether something is unmodifiable or not depends
on the object, not the lvalue expression used to modify it. And
arrays (at least non-const arrays) are certainly modifiable.
It's not arbitrary at all, and there is a perfectly good
rationale which can be derived from the definition of arrays.
Arrays effectively act as const pointers (in other words "int*
const"), the only exception being that the sizeof() operator
returns the space taken by the entire array rather than the
size of a pointer. (I know this is simplifying quite a lot,
and there are probably other examples where they don't
actually act like const pointers, but you get the idea.)
It's misleading, if not actually false. Arrays always act as
arrays. One characteristic of an array, however, is that it can
be implicitly converted into a pointer to the first element.
Not a const pointer, by the way, but since the result of the
conversion is an rvalue, there's no way of accessing the
resulting temporary in order to modify it.
The reason a = b is illegal if a is an array type is that
assignment to anything but a class type requires an rvalue on
the right hand side, and according to =A75.2/8:
Whenever an lvalue expression appears as an operand of
an operator that expects an rvalue for that operand, the
lvalue-to-rvalue (4.1), array-to-pointer (4.2), or
function-to-pointer (4.3) standard conversions are
applied to convert the expression to an rvalue.
Thus, any array type to the right of a built-in assignment is
converted to a pointer. And there's no way to convert this
pointer back to the array type required by the left hand side.
Note that this is only true for built-in assignment. If the
left hand side is a class type with an assignment operator which
takes a reference, then an rvalue is not expected, none of the
conversions in the cited paragraph take place, and if the array
type can be used to initialize the reference, it will be.
So you can think of "b = a;" as the attempt to modify 'b' to
make it point to the same place as 'a' is pointing to (because
that's what pointer assignment does). However, 'b' is const,
so you can't modify it to point anywhere else.
IIRC, C did say something along those lines---that both the left
side and the right side of assignments underwent the array to
pointer conversion. And since the results of a conversion
(other than to a reference type in C++) is an rvalue, the
requirement that the left hand side be an lvalue was violated.
(But arrays still don't "effectively act like pointers". Even
in C, array types are very different from pointers, and there is
still only an implicit conversion.)
The C++ rules are slightly different, although the differences
generally can't be seen without the use of features not
available in C. The most obvious case:
struct Toto
{
Toto& operator=(int (&a)[5]);
};
Toto a;
int b[5];
a = b; // legal...
A somewhat more surprising one:
int (&f())[5];
/*
probably better written:
typedef int Array[5];
Array& f();
*/
int a[5];
a = f(); // legal! ?
To be frank, this one surprises me so much that I suspect that
there's still a constraint somewhere that I've missed. Perhaps
the combination of [=A75.17/3] "If the left operand is not of
class type, the expression is implicitly converted (clause 4) to
the cv-unqualified type of the left operand" and/or [=A75/6] "If
an expression initially has the type =93reference to T=94 (8.3.2,
8.5.3), the type is adjusted to =93T=94 prior to any further
analysis, the expression designates the object or function
denoted by the reference, and the expression is an lvalue" imply
that the "reference" in the type is stripped off before the
check for rvalue-ness is applied.
Of course one could argue that the language should make an
exception to this general principle in the case where you are
assigning an array to another of the same type and size, and
make it instead assign the contents of the array to the other
array. I don't know, however, if the standardization committee
had a reason to not impose such an exception to the general
rule (and we are probably talking about the C standardization
committee here, as this limitation is probably coming from C).
The C++ committee designed the rules to be compatible with
standard C. (Allowing array assignment would have been a simple
extension, but it wouldn't really have bought much unless they
also allows passing arrays to functions, which would have broken
compatibility completely.) The C committee designed their rules
to be compatible with all of the existing implementations, which
were based on the rules defined in K&R1. Which in turn derived
from B, which didn't have any types---everything was a "word",
and when you declared the dimensions of an array, the compiler
allocated an anonymous array, and put its address in the named
variable (which had type word).
--
James Kanze