Почему `this` является зависимым от типа выражением, даже если класс шаблона не имеет базового класса?

Следующий код может быть скомпилирован без ошибок:

template <typename T> struct A {
    void f() { this->whatever; } // whatever is not declared before
};
int main() {
    A<int> a;
}

И я знаю это, потому что this - это зависящее от типа выражение, которое делает поиск имени для whatever отложен до тех пор, пока не будет известен фактический аргумент шаблона. Поскольку функция-член f() никогда не используется в этом случае, так что никакого экземпляра A<T>::f не существует, а поиск имен для whatever никогда не выполняется.

Я понимаю, что this зависит от типа, если шаблон класса имеет зависимую от типа базу, например:

template <typename T> struct B { T whatever; };
template <typename T> struct A : B<T> {
    void f() { this->whatever; }
};
int main() {
    A<int> a;
}

При анализе определения класса шаблона A невозможно узнать, какой тип его базы, что делает this->whatever потенциально законным (B<T> может иметь член с именем whatever). Напротив, я не вижу никакого потенциала, что this->whatever будет легальным в первом примере, как только функция-член f будет использоваться где-то.

Итак, может ли this->whatever быть законным в некоторых точках первого примера? Если нет, есть ли другая причина, согласно которой this следует рассматривать как зависимое от типа выражение в этом случае?

Ответ 1

Ваш код "плохо сформирован, не требуется диагностика", потому что для A::f никогда не существует актуальной специализации. Фактически, спецификация говорит, что this->whatever не является членом неизвестной специализации (потому что не существует зависимого базового класса), ни членом текущего экземпляра (поскольку он не объявлен в независящем базовом классе, ни в сам шаблон класса). Это, кроме того, делает ваш код недействительным, и снова диагностика не требуется (но разрешена). Это объясняется более подробно на fooobar.com/questions/11848/...

this зависит от типа, потому что вы еще не знаете значения параметра шаблона в определении. Поэтому, например, SomeOtherTemplate<decltype(*this)> не может быть разрешен немедленно, но ему нужно подождать, пока не будет создан шаблон класса this (так что вам нужно typename до SomeOtherTemplate<decltype(*this)>::type).

Однако только потому, что this зависит от типа, это не означает, что this->whatever также. Как описано выше, спецификация имеет инструменты, чтобы правильно классифицировать это как недействительные, и на самом деле также не делает тип this->whatever зависимым от типа. В нем говорится:

Выражение доступа к члену класса ([expr.ref]) зависит от типа, если выражение относится к члену текущего экземпляра, а тип ссылочного элемента зависит или выражение доступа к члену класса относится к элементу неизвестной специализации.

Ответ 2

Ваш пример может быть еще более упрощен:

template <typename T> struct A {
    void f() { this = 1; }
};
int main() {
    A<int> a;
}

Оператор this = 1; никогда не должен компилироваться и не может быть исправлен, даже если A<T> имеет базовый класс, зависящий от типа. Однако компилятор не жалуется, пока не будет создана функция A<T>::f().

Как Johannes Schaub - litb уже ответил , это может быть ситуация, "не требующая диагностики".

Ответ 3

Это правила поиска имени для зависимых имен.

$14.6/9 Разрешение имени [Temp.res]:

При поиске декларации имени, используемого в определении шаблона, обычные правила поиска ([basic.lookup.unqual], [basic.lookup.argdep]) используются для не зависимых имен. Поиск имен, зависящих от параметров шаблона, откладывается до тех пор, пока не будет известен фактический аргумент шаблона ([temp.dep]).

Цель состоит в том, что информации недостаточно, если имя зависит от параметра шаблона, пока не будет известен фактический аргумент шаблона. Компилятор не будет различать тип зависимых имен (сформированный this или другими), не будет проверять детали, как класс имеет зависимый базовый класс или нет. Результат может не измениться, как пример кода, который вы показали, но он просто откладывает поиск имени до тех пор, пока тип не будет известен, чтобы сделать наиболее точное решение.