Назначение этого указателя для ссылки на ссылку на указатель

Должен ли следующий образец скомпилировать?

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

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

int main(){}

На LWS с clang он компилируется, но с gcc я получаю:

нет известного преобразования для аргумента 1 из 'B * const' в 'B * &&'

и если я добавлю const он скомпилируется.

Я хотел бы также указать, что MSVC тоже ошибается:

не может преобразовать параметр 2 из 'B * const' в 'B * &&'

поэтому похоже, что у нас есть ошибка в двух компиляторах.

ОШИБКИ

Ссылка на сообщение MSVC

Ссылка на ошибку GCC

Ответ 1

Да, это должно скомпилировать.

Неправильно реализовать this как cv T* const (где cv - cv-квалификаторы для функции, если они есть, и T - тип класса). this не const, а просто выражение prvalue встроенного типа (не изменяемое).

Многие люди думают, что, поскольку вы не можете изменить this он должен быть const, но, как уже однажды прокомментировал Йоханнес Шауб-либб, гораздо лучшее объяснение выглядит примерно так:

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

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

Здесь ясно, что вы не можете изменить this (скажем, this = nullptr), но также ясно, что для такого объяснения не требуется const. (И значение, которое у вас есть в вашем конструкторе, - это просто значение временного.)

Ответ 2

Я говорю, что clang прав - код должен компилироваться. По какой-то причине GCC считает, что this указатель будет const несмотря на следующее:

Тип this в членной функции класса X есть X*. Если функция-член объявлена const, тип этого - const X*, если функция-член объявлена volatile, тип этого является volatile X*, и если функция-член объявлена const volatile, тип этого const volatile X*.

Таким образом, в этом случае this должно быть prvalue B* и идеально привязывается к B*&&. Однако обратите внимание, что при привязке this к ссылке rvalue значение this будет скопировано во временный объект, и ссылка будет привязана к этому. Это гарантирует, что вы никогда не изменяете оригинал this значения.

Ссылка на тип "cv1 T1 " инициализируется выражением типа "cv2 T2 " следующим образом:

  • [...]

  • [...] или ссылка должна быть ссылкой rvalue.

    • Если выражение инициализатора

      • это значение xvalue, класс prvalue, значение prvalue массива или функция lvalue и [...], или

      • имеет тип класса (т.е. T2 - тип класса), [...]

      тогда [...]

    • В противном случае временный тип "cv1 T1" создается и инициализируется из выражения инициализатора, используя правила для неосновной копии-инициализации (8.5). Ссылка затем привязана к временному. [...]