9

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.

12
  • "But since C++17 a is clearly specified as a modifiable lvalue (C++17 [basic.lval]/7)." -- May you quote that relevant text for a handy reference? Commented Aug 10, 2019 at 3:57
  • So you want to assign an uninitialized value to your variable ? Commented Aug 10, 2019 at 3:58
  • @iammilind see here Commented Aug 10, 2019 at 4:02
  • @SidS T{} initializes all array elements to 0 Commented Aug 10, 2019 at 4:02
  • 1
    The question was stolen from the std-discussion mail list. Commented Aug 10, 2019 at 12:38

3 Answers 3

8

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, ...

Sign up to request clarification or add additional context in comments.

2 Comments

One could argue that assignment to arrays is actually never prohibited, and if that is the case, then ... (mind blown)
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 think that someone who was introducing the modifiable lvalue definition into the C++ standard confused lvalues with objects.
0

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.

4 Comments

@LanguageLawyer That may be the absence of the rules for array type in basic.lval#7, because dcl.array#5 indicates that the object of array type should have to be an non-modifiable object.
I've removed this Note, check newer drafts. The history of modifiable lvalue is funny. AFAIK Richard Smith confused lvalues with objects, told that objects of array types can be modified, that's why lvalues of array types shouldn't be excluded from modifiable lvalues and changed the modifiable lvalue definition. And then he innocently writes in std-discussion that nothing in C++ forbids assignment to arrays. BTW, later he told that he meant that arrays are modifiable in colloquial sense, and in C++, only objects of scalar types can be modified.
@LanguageLawyer I don't think object of array types can be modified. Conversely, its elements can be modified, that means, the sub-objects of an object can be modified does not mean that such a complete object can be modified, they're two concepts.
0

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.

12 Comments

Notes are non-normative; also points 2.1, 2.3 and 2.6 specify some cases of materialization of a prvalue array . The case auto&& b = T{}; would come under point 2.1 , materializing a prvalue array.
@M.M "Notes are non-normative" See my question here. In reference to your next comment I should have said "no provision for the materialization of arrays in assignments.
Which of those clauses covers the materialization in int c; c = int{}; ?
That doesn't say anything about temporary materialization
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.