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

#c #friend #extern

#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" необходимо.

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

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

Комментарии:

1. Не тот случай без анонимного пространства имен. И вот тестовый пример.

2. Хм, тогда это ошибка? Я не могу найти в правилах поиска ничего, что объясняло бы, почему для этой комбинации extern "C" и anonymous namespace потребуется :: , но удаление любого из них делает его ненужным. По общему признанию, хотя мои знания о более тонких деталях поиска имен туманны, и это было основано на поиске.

3. кстати: я пробовал с gcc 4.4.0 4.4.1 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.5.0 4.5.1 4.5.2 4.5.3 4.6.0 4.6.1 4.7.0 и все они компилируются с :: и терпят неудачу без.

4. Пункт 2 кажется неправильным. Код не компилируется для меня, даже если foo это не extern "C" так.

5. @PlasmaHH заслужил более одного жалкого голоса за тестирование с помощью 14 наборов инструментов, люди, да ладно! (даже если он делает сценарий * гм *)

Ответ №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.
*/
  

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

Комментарии:

1. (Что касается почему , ну, кто знает, черт возьми)