Дружба с внешней функцией "C", по-видимому, требует: • квалифицировать имя

Попытка создать друзей class с помощью функции extern "C", этот код работает:

#include <iostream>

extern "C" {
  void foo();
}

namespace {
  struct bar {
    // without :: this refuses to compile
    friend void ::foo();
    bar() : v(666) {}
  private:
    int v;
  } inst;
}

int main() {
  foo();
}

extern "C" {
  void foo() {
    std::cout << inst.v << std::endl;
  }
}

Но я был очень удивлен, обнаружив, что с g++ 4.6.1 и 4.4.4 я должен явно писать :: в friend void ::foo();, иначе дружба не сработает. Этот :: нужен только тогда, когда он extern "C".

  • Является ли это ошибкой/проблемой компилятора? Я не ожидал такого поведения.
  • Если это не ошибка, почему это требуется, но только когда она extern "C" и не без нее? Как насчет изменений правил поиска имен, которые делают это необходимым?

Я в тупике. Вероятно, для этого есть правило, которое я не могу найти.

Ответ 1

[n3290: 7.3.1.2/3]: Каждое имя, впервые объявленное в пространстве имен, является член этого пространства имен. Если объявление friend в нелокальном классе сначала объявляет класс или функцию, класс или функция друга член самого внутреннего охватывающего пространства имен. Имя друга не найденный неквалифицированным поиском (3.4.1) или квалифицированным поиском (3.4.3) 95) это означает, что имя класса или функции неквалифицированный. пока соответствующая декларация не будет предоставлена ​​в пространство имен (до или после предоставления определения класса дружба). Если вызывается функция друга, ее имя может быть найдено поиск имени, который рассматривает функции из пространств имен и классов связанные с типами аргументов функции (3.4.2). Если имя в объявлении friend не является ни квалифицированным, ни идентификатором шаблона и декларация является функцией или специфицированным спецификатором типа, поиск, чтобы определить, было ли ранее объявлено сущность не должны рассматривать какие-либо области вне самого внутреннего пространство имен. [..]

Внутрипространственное пространство имен является анонимным, и вы не определили имя функции, поэтому имя не найдено.

Пространство имен не обязательно должно быть анонимным.

Обратите внимание, что extern "C" в вопросе - это красная селедка, а по той же причине не работает следующее:

void foo();

namespace {
struct T {
   friend void foo();

   private: void bar() { cout << "!"; }
} t;
}

void foo() { t.bar(); }

int main() {
   foo();
}

/*
In function 'void foo()':
Line 7: error: 'void<unnamed>::T::bar()' is private
compilation terminated due to -Wfatal-errors.
*/

[альтернативный тест, адаптированный из вашего исходного кода]