12
votes

It appears that C++11 and C++14 treat cv-qualifications of prvalues differently.

C++11 sticks to the "classic" approach that has been around since C++98: according to 3.10/4 "non-class prvalues always have cv-unqualified types".

C++14 contains a similar wording in 3.10/4, but it is presented as a note: "[Note: class and array prvalues can have cv-qualified types; other prvalues always have cv-unqualified types. See Clause 5. —end note ]"

And in Clause 5 it says:

6 If a prvalue initially has the type “cv T,” where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.1

This 5/6 entry is new in C++14. It now treats cv-qualifications of prvalues using the same approach that has always been used with results of reference type (see 5/5).

What could be the reason for this change? C++11 and before denied non-class prvalues the right to have any cv-qualifications. C++14 says that non-class, non-array prvalues can have cv-qualifications, but these cv-qualifications are discarded prior to any further analysis.

My guess would be that there are some new (for C++14) language features that can somehow "see" cv-qualifications of prvalues under the right circumstances (before the aforementioned adjustment takes place). Do they exist? And if so, what are these features?2


The question originated from following context: imagine a compiler that internally implements hidden parameter this of class X as a variable of type X *const. Since the compiler is required to expose this as a prvalue, that const should not lead to any problems in C++11 (or before), where scalar prvalues are never cv-qualified. But what about C++14? If the very same compiler exposes that this as a prvalue of type X *const, could it possibly lead to problems?


1 There appears to be a contradiction between 5/6 and the note in 3.10/4 in C++14, but notes are not normative anyway. And I'm using a draft version of the text.

2 My initial guess was decltype. And I even thought that I found the answer when I tried

std::cout << std::is_same<decltype((const int) 0), const int>::value << std::endl;

in GCC, which outputs 1. However, seeing that Clang and VC++ output 0 (and that the spec of decltype does not seem to support this behavior) I'm inclined to believe that this is just a bug in GCC (starting from 6.1)

1
@Neil Butterworth: This is a question about conceptual behavior. The whole reason I'm asking the question is that I cannot find any code example that would exploit the above difference between C++11 and C++14. If the difference cannot be demonstrated by code, then I'd like to know what prompted the change in the standard text. If the difference can be demonstrated by code, then it would be nice to see that code. - AnT
@Thomas This is definitely language lawyer territory. - user2100815
@Neil Butterworth: Great. So, in what regard is the new wording "more accurate or nuanced"? That's basically what my question is about. - AnT
This was core issue 1261, which has zero discussion in the public issue list, but I think it's just wording cleanup. E.g., given const int f();, f() is a prvalue and its type is, according to [expr.call], "the return type of the statically chosen function", which would be const int, and some sort of wording is needed to make that const go away. - T.C.
Well yes, GCC used to have problems with code like S*&& p = this;. Now they don't. - T.C.

1 Answers

3
votes

According to the commit on github, this was done to resolve CWG1261: Explicit handling of cv-qualification with non-class prvalues

Based on comments to the question it seems there was room for surprising variations in type category of this (formally a prvalue) and that gcc formerly and MSVC currently instead used a const lvalue.

The wording tightens up the hole to be explicit that, e.g., even if this is by some compiler-internal magic a prvalue of type X* const, prior to any further analysis it is adjusted to X*.

Similarly, your given example does look like a gcc bug. Possibly decltype isn't looking at the value type before applying the c-style cast.

The reason it's now a note in [basic.lval]/4 is that it's now a consequence of the new text in [expr]/6, rather than specifying the rule in [basic.lval]/4.

Full credit to T.C. for having basically answered this in the comments on the question, including the reference to the gcc bug-fix, and various other examples of previously under-specified behaviours for cv-qualified non-class non-array prvalues.