Почему вводится имя класса?
Недавно я увидел странную особенность C++: введенное имя класса.
class X { };
X x1;
class X::X x2; // class X::X is equal to X
class X::X::X x3; // ...and so on...
Но я не могу понять, почему эта функция необходима. Есть ли практика, которая требует этой функции?
И я слышал, что эта функция не существовала в старом C++. Тогда когда это было введено? C++03? C++11?
1 ответ
Введенное имя класса означает, что X
объявлен членом X
, так что имя ищем внутри X
всегда находит текущий класс, а не другой X
которые могут быть объявлены в одной и той же области видимости, например
void X() { }
class X {
public:
static X create() { return X(); }
};
Это create()
функция создания временного X
объект или вызов функции X
? В области имен он будет вызывать функцию, поэтому цель имени введенного класса состоит в том, чтобы гарантировать, что внутри тела X
имя всегда находит сам класс (потому что поиск имени начинается в собственной области видимости класса перед тем, как искать в окружающей области).
Это также полезно в шаблонах классов, где введенное имя класса можно использовать без списка аргументов шаблона, например, используя просто Foo
вместо полного идентификатора шаблона Foo<blah, blah, blah>
, так что легко обратиться к текущему экземпляру. См. DR 176 для изменения между C++98 и C++03, который прояснил это.
Идея внедренного имени класса была представлена в C++98, но терминология была новой для C++03.
C++98 говорит:
Имя класса вставляется в область, в которой оно объявляется сразу после того, как имя класса замечено. Имя класса также вставляется в область действия самого класса.
Второе предложение было изменено DR 147, поэтому C++03 говорит в [class]/2:
Имя класса вставляется в область, в которой оно объявляется сразу после того, как имя класса замечено. Имя класса также вставляется в область действия самого класса; это известно как имя введенного класса.
Даже до C++98 ARM имеет примерно эквивалентную формулировку, которая означает, что имя класса всегда можно использовать в теле класса для ссылки на сам класс:
Имя класса может использоваться в качестве имени класса даже внутри списка членов самого спецификатора класса.
- Например,
class link { link* next; };