Что происходит, когда происходит Injected-Class-Name? (С++)

Согласно https://en.cppreference.com/w/cpp/language/injected-class-name

В области класса имя текущего класса обрабатывается так, как если бы оно было общедоступным именем члена; это называется имя-внедренного-класса. Точка объявления имени находится сразу после открывающей скобки определения класса.

int X;
struct X {
    void f() {
        X* p; // OK. X refers to the injected-class-name
        ::X* q; // Error: name lookup finds a variable name, which hides the struct name
    }
};

Так что же на самом деле происходит в коде? X* p превратилось в X::X* p?


person csguy    schedule 28.01.2020    source источник


Ответы (2)


Так что же на самом деле происходит в коде? X* p превращается в X::X* p?

В принципе. Правила поиска имен начинаются с самой узкой области. Когда вы делаете X* p; в f, он смотрит в область f и ничего не находит. Затем он проверяет область действия X, так как f имеет область действия X. Он находит X, поскольку он вводится в область класса, поэтому он останавливается на этом, и вы получаете тип класса.

Когда вы выполняете ::X* q;, то ::X говорит, что ищите X в глобальном пространстве имен, и находит переменную, а не тип, поэтому вы получаете сообщение об ошибке.

person NathanOliver    schedule 28.01.2020
comment
Итак, причина, по которой может произойти что-то вроде X::X::foo (что-то вроде: «почему код foofoofooofoob компилируется»> stackoverflow.com/questions/46805449/) потому, что Injected-Class-Name игнорирует эти поиски области и просто выполняет X::foo? - person csguy; 28.01.2020
comment
@csguy Он не игнорирует его как таковой, просто в данном случае X:: содержит X и f. X, потому что он вводится через компилятор, и f, потому что вы его определили. Если вы сделаете X::X, вы вернетесь к тому, с чего начали, и снова получите X и f в качестве допустимых имен. Есть смысл? - person NathanOliver; 28.01.2020
comment
Ага, понятно. X вводится, потому что оно не указано как имя члена, потому что это просто имя класса? - person csguy; 28.01.2020
comment
@csguy Когда вы создаете класс, компилятор делает имя класса членом класса. Таким образом, class_name::class_name всегда действителен, а поскольку class_name::class_name является class_name, вы можете снова получить доступ к его членам и продолжить, как X::X::X::X::X::X::X::X::X::X::X::X::X::f();, если вы действительно этого хотите. - person NathanOliver; 28.01.2020
comment
И это именно из-за Injected-Class-Name, верно? - person csguy; 28.01.2020
comment
Да. Если бы имя не было введено, то class_name::class_name было бы недействительным (конструкторы недоступны) - person NathanOliver; 28.01.2020
comment
Теперь, когда я понимаю это, ссылаясь на ваш ответ, когда вы говорите, что X* p по существу становится X::X* p, это потому, что X вводится как член класса, поэтому переменная, объявленная с помощью X, будет ссылаться на нее правильно? Это будет более заметно в шаблонах, где, например, введен тип X<T>, а все X в шаблоне будут ссылаться на X<T>. - person csguy; 28.01.2020
comment
@csguy Ага. Разве С++ не забавен? ;) - person NathanOliver; 28.01.2020

Это полное имя ::X ищется в глобальном пространстве имен. Поскольку типа с таким именем нет (объявление переменной скрывает тип struct X), компилятор выдает ошибку.

Вы можете использовать сложное имя, например

int X;
struct X {
    void f() {
        X* p; // OK. X refers to the injected-class-name
        struct ::X* q; // OK. elaborated name struct ::X
    }
};
person Vlad from Moscow    schedule 28.01.2020