#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"
необходимо.
- Это ошибка / проблема компилятора? Я не ожидал такого поведения.
- Если это не ошибка, почему это требуется, но только тогда, когда это
extern "C"
так, а не без этого? Как насчет изменений правил поиска имен, которые делают это необходимым?
Я в тупике. Вероятно, для этого есть какое-то правило, которое я не могу найти.
Комментарии:
1. Не тот случай без анонимного пространства имен. И вот тестовый пример.
2. Хм, тогда это ошибка? Я не могу найти в правилах поиска ничего, что объясняло бы, почему для этой комбинации
extern "C"
и anonymousnamespace
потребуется::
, но удаление любого из них делает его ненужным. По общему признанию, хотя мои знания о более тонких деталях поиска имен туманны, и это было основано на поиске.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. (Что касается почему , ну, кто знает, черт возьми)