12

Should the following sample compile?

struct B;
struct A
{
  A(B*&&){}
};

struct B : A
{
  B() : A(this){}
};

int main(){}

On LWS with clang it compiles, but with gcc I get:

no known conversion for argument 1 from 'B* const' to 'B*&&'

and if I add a const it compiles.

I would like to also point out MSVC gets it wrong too:

cannot convert parameter 2 from 'B *const ' to 'B *&&'

so it looks like we have a bug in two compilers.

BUGS FILED

MSVC bug link

GCC bug link

Jesse Good
  • 48,564
  • 14
  • 115
  • 165

2 Answers2

8

Yes, that should compile.

It is incorrect to implement this as cv T* const (where cv is the cv-qualifiers for the function, if any, and T is the class type). this is not const, merely a prvalue expression of a built-in type (not modifiable).

Many people think that because you can't modify this it must be const, but as Johannes Schaub - litb once commented long ago, a much better explanation is something like this:

// by the compiler
#define this (__this + 0)

// where __this is the "real" value of this

Here it's clear that you can't modify this (say, this = nullptr), but also clear no const is necessary for such an explanation. (And the value you have in your constructor is just the value of the temporary.)

Community
  • 1
  • 1
GManNickG
  • 478,574
  • 51
  • 478
  • 539
  • Is it actually specified that we can't modify `this`? Obviously we can't when it's a prvalue, but what if we bind it to an rvalue reference and modify it through that? – Joseph Mansfield Mar 21 '13 at 21:57
  • Interesting, so that means [the selected answer to this SO question](http://stackoverflow.com/questions/7341607/so-what-is-the-type-of-this-why-is-this-not-a-lvalue) is wrong? – Jesse Good Mar 21 '13 at 22:00
  • @sftrabbit: It's a side-effect of it being a prvalue and a built-in type. Such objects cannot be treated as lvalues (assigned to or modified in general). This doesn't apply to user-defined types because then we have operator overloading and what have you. You can invoke a member function on an rvalue, for example. – GManNickG Mar 21 '13 at 22:02
  • @JesseGood: Yes. I edited it. (Heh, I left a comment on that question with the same `#define this` example, almost two years ago.) – GManNickG Mar 21 '13 at 22:08
  • 1
    @GManNickG I've figured it out. Binding an rvalue reference to `this` will first create a temporary copy of it and then bind to that. So the original `this` pointer can't actually be affected. – Joseph Mansfield Mar 21 '13 at 22:18
  • 2
    @sftrabbit: Yup! Same as: `int&& x = 0;`. – GManNickG Mar 21 '13 at 22:22
  • 2
    @GmanNickG Yeah, I just tried `int&& x = 5; x++;` and for a moment thought I'd changed math itself. Now we shall learn to count with 1, 2, 3, 4, 6, 6, 7, ... – Joseph Mansfield Mar 21 '13 at 22:27
  • It was in this comment: http://stackoverflow.com/questions/385297/whats-wrong-with-c-compared-to-other-languages#comment214413_385316 (deleted, but still readable in the quotes here: http://stackoverflow.com/questions/645994/why-this-is-a-pointer-and-not-a-reference ) – Johannes Schaub - litb Mar 22 '13 at 15:05
  • @JohannesSchaub-litb: Ha, nice find. :) – GManNickG Mar 22 '13 at 15:08
6

I say clang is right - the code should compile. For some reason, GCC considers the this pointer to be const despite the following:

The type of this in a member function of a class X is X*. If the member function is declared const, the type of this is const X*, if the member function is declared volatile, the type of this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile X*.

So in this case, this should be a prvalue B* and perfectly bindable to B*&&. However, note that when binding this to an rvalue reference, the value of this will be copied into a temporary object and the reference will instead be bound to that. This ensures that you never actually modify the original this value.

A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:

  • [...]

  • [...] or the reference shall be an rvalue reference.

    • If the initializer expression

      • is an xvalue, class prvalue, array prvalue or function lvalue and [...], or

      • has a class type (i.e., T2 is a class type), [...]

      then [...]

    • Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the temporary. [...]

Joseph Mansfield
  • 104,685
  • 19
  • 232
  • 315
  • Maybe because you're in the constructor? – Kerrek SB Mar 21 '13 at 21:46
  • @KerrekSB I don't see a rule that says that, but still, why would that make the *pointer* `const`? – Joseph Mansfield Mar 21 '13 at 21:48
  • Hm, scratch that... but notice that `this` is an *expression*, not a variable. It's a prvalue. – Kerrek SB Mar 21 '13 at 21:49
  • Your argument is invalid. Gcc doesn't say that `this` is `const B*`. It says it's a `B* const` which is not the same thing! `this` is a prvalue, so `B*` and `B* const` are basically the same thing – Armen Tsirunyan Mar 21 '13 at 21:53
  • 1
    @ArmenTsirunyan That's not true. A prvalue of type `B* const` wouldn't bind to a `B*&&`, whereas a prvalue of type `B*` would. – Joseph Mansfield Mar 21 '13 at 21:54
  • @sftrabbit: Be that as it may, all I am saying is that the quote you provided is irrelevant because it speaks of another const. In any case I don't think that a **prvalue** of type `B*` can be bound to `B*&&` either. I may be wrong though – Armen Tsirunyan Mar 21 '13 at 21:57
  • @ArmenTsirunyan: How is the quote irrelevant? It says what type `this` is, which conflicts with what GCC says its type is. – Benjamin Lindley Mar 21 '13 at 22:01
  • @ArmenTsirunyan: It may not say explicitly "there is no const on the pointer, by the way", but it does omit it which is equivalent (and on the previous line in the standard says its a prvalue). – GManNickG Mar 21 '13 at 22:04
  • 1
    I forgot that there also is 3.10p4: `non-class prvalues always have cv-unqualified types`. This means a prvalue of `B*` can never be `const`. – Jesse Good Mar 21 '13 at 22:57
  • @GManNickG: Maybe I am missing something, but I started to think Armen Tsirunyan point stands, as a `prvalue always have cv-unqualified types` as from my previous quote. – Jesse Good Mar 21 '13 at 23:02
  • @JesseGood: I'm not sure I follow. GCC/MSVC have a bug so it's *not* a prvalue, so they can add `const`. – GManNickG Mar 21 '13 at 23:04
  • @JesseGood That means `this` is for sure a `B*` (not a `B* const`) and can therefore be bound to `B*&&`. GCC is saying that `this` is a `B* const`, which goes against the standard. – Joseph Mansfield Mar 21 '13 at 23:06
  • @GManNickG, sftrabbit: Right, it still is a bug in both compilers. I was thinking in terms of the underlying type of `this` could be `T* const` per the standard, but that is thinking of `this` as an object and not an expression which is wrong. – Jesse Good Mar 21 '13 at 23:19