13

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)

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Actual code examples regarding what you are talking about are always nice. –  Mar 23 '17 at 23:55
  • 5
    @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 stands with Russia Mar 23 '17 at 23:58
  • I dislike the "language-lawyer" tag. It trivializes. – ThomasMcLeod Mar 24 '17 at 00:02
  • 4
    @Thomas This is definitely language lawyer territory. –  Mar 24 '17 at 00:05
  • @AnT so your question is why was the wording changed? Lots of the wording changed, to perhaps be more accurate or nuanced?. –  Mar 24 '17 at 00:08
  • 5
    @Neil Butterworth: Great. So, in what regard is the new wording "more accurate or nuanced"? That's basically what my question is about. – AnT stands with Russia Mar 24 '17 at 00:13
  • 4
    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. Mar 24 '17 at 01:10
  • 1
    As to `this`, the standard exhaustively specifies the type of `this`. Anything else is nonconforming, period. And no, nothing can see the pre-adjustment type; that's what "prior to any further analysis" means. This is why people often say "an expression never has reference type". – T.C. Mar 24 '17 at 03:35
  • 2
    @T.C.: I'm talking about internal implementation details/tricks. GCC up to version 4.8.5 was known to react to `int *p = this` code with "error: cannot convert 'S* const' to 'int*' in initialization" message (note the `const`). VC++ does the same thing to this day. Apparently, this is/was a rather popular implementational trick. And as long as no language feature can detect this "non-conformance", these compilers are free to use that trick. GCC diagnostic message no longer mentions `const` in this case. Maybe they abandoned the practice, or maybe they just tweaked the diagnostic message... – AnT stands with Russia Mar 24 '17 at 06:46
  • 4
    Well yes, GCC used to have problems with code like `S*&& p = this;`. Now they don't. – T.C. Mar 24 '17 at 06:56
  • @T.C.: Thank you! Great example! VC++ also fails on this initialization. – AnT stands with Russia Mar 24 '17 at 07:00
  • So apparently [GCC's internal representation of `this` was a const lvalue](https://gcc.gnu.org/ml/gcc-patches/2013-03/msg01231.html). – T.C. Mar 24 '17 at 20:59
  • @T.C. Unless GCC used to allow `&this` as an extension, which would be surprising, it couldn't be a true lvalue. – curiousguy Jan 28 '19 at 00:24

1 Answers1

3

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.

Community
  • 1
  • 1
TBBle
  • 1,436
  • 10
  • 27
  • Jason Merrill wrote "_When 'this' appears in an expression, it should be an rvalue rather than a const lvalue; in C++11, the distinction matters._" C++11? An rvalue was never like a const lvalue in any point of C++ history. – curiousguy Jan 28 '19 at 00:26
  • @curiousguy; Prior to C++11 you couldn’t use `decltype` and rvalue references to distinguish them (but maybe `const_cast` could have done it?). – Davis Herring Jan 29 '19 at 04:13
  • @DavisHerring You mean `C * const &that = this; const_cast(that) = new C;`? – curiousguy Jan 29 '19 at 06:53
  • 1
    @curiousguy: Just `const_cast(this)`—assigning to it might be undefined behavior, but if it’s an rvalue then the cast shouldn’t compile (if I remember my C++03 correctly). – Davis Herring Jan 29 '19 at 07:51
  • @DavisHerring Or simply `&this` – curiousguy Jan 29 '19 at 07:55
  • @curiousguy: It could theoretically be a temporary, I guess, but it makes sense. – Davis Herring Jan 29 '19 at 14:12