Почему есть введенное имя класса?

Недавно я увидел странную С++-функцию: имя введенного класса.

class X { };
X x1;
class X::X x2; // class X::X is equal to X
class X::X::X x3; // ...and so on...

Но я не могу понять, почему эта функция необходима. Существует ли какая-либо практика, требующая этой функции?

И я слышал, что эта функция не существовала в старом С++. Тогда, когда это было введено? С++ 03? С++ 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 для изменения между С++ 98 и С++ 03, которые разъяснили это.

Идея введенного имени класса присутствовала в С++ 98, но терминология была новой для С++ 03.

С++ 98 говорит:

Имя класса вставляется в область, в которой объявляется сразу после просмотра имени класса. Имя класса также вставляется в область самого класса.

Второе предложение было изменено DR 147, поэтому С++ 03 говорит в [class]/2:

Имя класса вставляется в область, в которой объявляется сразу после просмотра имени класса. Имя класса также вставляется в область самого класса; это известно как имя введенного класса.

Даже до С++ 98 ARM имеет примерно эквивалентную формулировку, которая означает, что имя класса всегда можно использовать в теле класса для обозначения самого класса:

Имя класса может использоваться как имя класса даже внутри самого списка участников самого спецификатора класса.

  •   
  • Например,      
      

    class link { link* next; };