, Если бы я запустил свой очень собственный главный проект программного обеспечения теперь, я принял бы свое решение языка на основе критериев выше. Несомненно, я люблю Lisp, CLOS является потрясающими, реальными лексическими скалами обзора, макросы Lisp прохладный путь (когда используется, как направлено), и лично мне действительно нравится синтаксис Lisp. [†¦], Но это взяло бы много или потребовало бы особых обстоятельств, чтобы убедить меня выбрать Lisp для главного проекта программного обеспечения, если бы я ответил за совершение выбора. - Dan Weinreb
Если я правильно прочитал стандарт, g ++ верен, а VS - нет.
ISO-IEC 14882-2003 (E), §9.2 Классы (pag.153) : Имя класса вставляется в область видимости, в которой оно объявлено, сразу после того, как будет обнаружено имя класса. Имя класса также вставляется в область видимости самого класса; это известно как внедренное имя-класса. В целях проверки доступа внедренное имя-класса обрабатывается так, как если бы оно было открытым именем члена.
Следуя комментариям ниже, также особенно полезно сохранить следующее, касающееся фактических правил поиска имени:
ИСО-МЭК 14882-2003 (E), §3.4-3 Поиск имени (стр.29) : Введенное имя класса класса (пункт 9) также считается членом этого класс для скрытия имени и поиска.
Было бы странно, если бы это было не так, учитывая последнюю часть текста в 9.2. Но, как прокомментировал литб, это убеждает нас в том, что действительно g ++ правильно интерпретирует стандарт. Не осталось вопросов.
Есть ли пространство имен foo в каком-то другом модуле, который вы включаете (а вы просто не знали о нем)? В противном случае это неверно. Я не уверен, почему g ++ это допускает.
Кругар дает здесь правильный ответ. Имя, которое обнаруживается каждый раз, является именем внедренного класса.
Ниже приводится пример, который показывает по крайней мере одну причину, по которой компилятор добавляет имя внедренного класса:
namespace NS
{
class B
{
// injected name B // #1
public:
void foo ();
};
int i; // #2
}
class B // #3
{
public:
void foo ();
};
int i; // #4
class A :: NS::B
{
public:
void bar ()
{
++i; // Lookup for 'i' searches scope of
// 'A', then in base 'NS::B' and
// finally in '::'. Finds #4
B & b = *this; // Lookup for 'B' searches scope of 'A'
// then in base 'NS::B' and finds #1
// the injected name 'B'.
}
};
Без введенного имени текущие правила поиска будут в конечном итоге достигнет охватывающей области «A» и найдет «:: B», а не «NS :: B». Поэтому нам нужно будет использовать «NS :: B» везде в A, когда мы хотим ссылаться на базовый класс.
Еще одно место, где используются внедренные имена, - это шаблоны, где внутри шаблона класса внедренное имя обеспечивает соответствие между именем шаблона и типом:
template <typename T>
class A
{
// First injected name 'A<T>'
// Additional injected name 'A' maps to 'A<T>'
public:
void foo ()
{
// '::A' here is the template name
// 'A' is the type 'A<T>'
// 'A<T>' is also the type 'A<T>'
}
};
Comeau online принимает его без каких-либо проблем, так что это либо действительный, либо вторая ошибка в como, которую я обнаружил почти за десять лет.