дружба с функцией extern "C", по-видимому, требует :: для определения имени - PullRequest
13 голосов
/ 18 октября 2011

Пытаясь подружиться 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".

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

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

1 Ответ

11 голосов
/ 18 октября 2011

[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.
*/

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

...