Clang vs. GCC: дружественная глобальная функция с помощью квалифицированных/неквалифицированных имен

Я пытаюсь выяснить, следует ли указывать отчет об ошибках против Clang, GCC или обоих (я тестировал ствол Clang и GCC 4.7.2: если бы кто-то мог проверить это в отношении соединительной линии GCC, что было бы полезно)

В принципе, следующий трехстрочный файл кода отлично компилируется с помощью -fsyntax-only, в режимах по умолчанию и С++ 11:

class A {
    friend void f();
};

Обратите внимание, что нет предварительного объявления f(), но это явно ОК.

Однако Clang (но не GCC) отклоняет следующее:

class A {
    friend void ::f();
};

Ошибка от Clang - "no function с именем" f "с типом" void() "была найдена в указанной области", но я не могу найти никакого оправдания в стандарте для рассмотрения этого случая иначе, чем предыдущий, поэтому я думаю, что это ошибка; Возможно, я ошибаюсь (я читаю из N3242, однако AFAIK - это последний публичный проект до С++ 11).

Этот следующий пример, однако, отклоняется GCC, а не Clang:

void f() { }

void g()
{
    class A {
        friend void ::f();
    };
}

Ошибка GCC - это "объявление друга" void f() в локальном классе без предварительного объявления ", что, похоже, не имеет смысла, поскольку void ::f() должно ссылаться на f() в глобальном пространстве имен, которое объявлен. (Не обращайте внимания на то, что friend - глобальная функция из локального класса является бессмысленной, она не кажется незаконной...)

Наконец, этот последний пример отклоняется как Clang, так и GCC:

void g()
{
    class A {
        friend void ::f();
    };
}

Ошибки - это "объявление друга" void f() в локальном классе без предварительного объявления ", а" no function с именем "f" с типом "void()" не было найдено в указанной области "соответственно. Теперь, похоже, есть некоторые оправдания для отклонения объявления друга ранее не объявленной функции в локальном классе, начиная с 11.4p11, но в абзаце на самом деле читается:

Если декларация друга появляется в локальном классе (9.8) , а указанное имя является неквалифицированным именем, предыдущий декларация просматривается без учета областей, которые находятся вне самой внутренней охватывающей неклассовой области. Для объявления функции друга, если нет предшествующего объявления, программа плохо сформирована...

Незаконность этого заявления, по-видимому, основана на втором предложении в этом параграфе, но мне неясно, должно ли предложение применяться в случае с квалифицированным именем, как это используется в этом случае. (Возможно, может быть, что весь абзац применяется к делу "локального класса", независимо от того, используется ли "неквалифицированное имя", а предложение "и указанное имя является неквалифицированным именем" применяется только к описанному выше описанию в первом предложении, но это похоже на растяжку... единственная причина, по которой я представляю это как возможность, заключается в том, что оба компилятора отвергают ее.)

Во всяком случае, так как я могу сказать, все четыре из этих случаев (независимо от того, насколько они разумны или нет) должны быть законными; даже если нет, по крайней мере один из Clang и GCC ошибочен во втором и третьем случаях. Может ли кто-нибудь найти ошибку в моей логике?

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

Ответы

Ответ 1

Я думаю, что это предложение из 8.3/1 имеет значение:

Когда идентификатор объявления является квалифицированным, декларация относится к ранее объявленному члену класса или пространства имен, к которому относится квалификатор (или, в случае пространства имен, элемента встроенного пространства имен, указанного в этом пространство имен (7.3.1)) или его специализацию; член не должен просто вводиться с помощью объявления-декларации в области класса или пространства имен, назначаемого идентификатором-вложенным именем идентификатора-декларатора.

В параграфе говорится о деклараторах вообще, в любом типе декларации. Поэтому я думаю, что примеры 2 и 4 плохо сформированы, clang является правильным, а gcc ошибочным.

(Пример 3 может стать более реалистичным, если глобальное объявление было функцией шаблона. Возможно, было бы полезно сопоставить глобальный шаблон функции или специализацию из локального класса. И если это разрешено, ваш пример 3, как написано, быть законным для согласованности.)