M.M
M.M

Reputation: 141628

Assignment to array in C++17

Here is some code:

int main()
{
    using T = int[3];
    T a;
    a = T{};
}

As far as I can tell, this code is correct according to the C++17 Standard, however every compiler I tried rejected it.

Is this code actually incorrect? If so, by what clauses of the Standard?


My investigation so far: In C and in older versions of C++, the code was incorrect because the assignment operator's left operand must be a modifiable lvalue, which a either wasn't, or it was unclearly specified. But since C++17 a is clearly specified as a modifiable lvalue (C++17 [basic.lval]/7).

The array-to-pointer conversion is not applied here: [expr.ass] doesn't explicitly specify it, and [expr]/9 and [expr]/10 don't seem to apply: the = expects a prvalue as right operand, and a prvalue was provided. (And it expects a glvalue as left operand, and a glvalue was provided). Those clauses apply if a glvalue was supplied where a prvalue was expected or vice versa.

[expr.ass]/3 says the right expression is implicitly converted to the type of the left operand . But since both sides have identical type int[3] no conversion seems to be necessary.

So I see no clauses which would exclude [expr.ass]/2 from applying, that the value of the right-hand side is stored in the object referred to by the left.


The latest draft moves around the clauses that were in [basic.lval]/7 and [expr]/9-10 but doesn't seem to change their meaning, and it even re-words [expr.ass]/2 to be clearer:

In simple assignment (=), the object referred to by the left operand is modified by replacing its value with the result of the right operand.

Upvotes: 9

Views: 508

Answers (3)

Belloc
Belloc

Reputation: 6390

There is no provision in the C++ Standard for the materialization of a prvalue array, as you can see in Note 3 of [class.temporary]/5, which summarizes the cases where these materializations occur.

Upvotes: 0

xmh0511
xmh0511

Reputation: 7369

Because built-in operators are also governed by [over.built], that is:

The candidate operator functions that represent the built-in operators defined in Clause [expr] are specified in this subclause.

For assignment operator, the forms of the corresponding functions are:
over.built#19

For every triple (L, vq, R), where L is an arithmetic type, and R is a promoted arithmetic type, there exist candidate operator functions of the form

For every pair (T, vq), where T is any type, there exist candidate operator functions of the form

Tvq& operator=(T vq&, T*);

For every pair (T, vq), where T is an enumeration or pointer to member type, there exist candidate operator functions of the form

vq T& operator=(vq T&, T);

Hence, neither of them could be as the candidate function when the corresponding arguments are a, T{}. So, the program should be ill-formed.

Upvotes: 0

eerorika
eerorika

Reputation: 238401

As far as I can tell, the definition of "modifiable lvalue" is either under-specified in C++, or arrays have been intentionally been specified to be assignable (I suspect that former is true, since no compiler does latter).

The standard (latest draft) says:

[basic.lval]

An lvalue is modifiable unless its type is const-qualified or is a function type.

This is quite concise, but there is no exclusion of arrays.

Furthermore, this hasn't changed through standard versions at least since C++03, which specifies following:

[basic.lval]

11 Functions cannot be modified, but pointers to functions can be modifiable.

12 A pointer to an incomplete type can be modifiable. ...

13 The referent of a const-qualified expression shall not be modified ...

Which is mostly same, except using more descriptive than definitive wording. No exclusion of arrays.


By contrast, C11 standard is crystal clear (quoting N1548 draft):

6.3.2.1 Lvalues, arrays, and function designators

1 ... A modifiable lvalue is an lvalue that does not have array type, ...

Upvotes: 8

Related Questions