Форум » C/C++ » Еще раз о поиске имен и объявлении дружественных функций » Ответить

Еще раз о поиске имен и объявлении дружественных функций

Сыроежка: Согласно стандарту С++ имя друга (функции или класса), объявленного в некотором классе не будет видно до тех пор, пока само объявление дружественной функции или класса не появится в соответствующем пространстве имен. Чтобы было понятно, то приведу простой пример [pre2]namespace N1 { struct A { friend void f(); }; // здесь имя f не видно void f(); // здесь имя f становится видным }[/pre2] Процитирую соответствующий параграф из стандарта С++ 2011. 7.3.1.2 "Namespace member definitions" #3 "...The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship)...." Эту фразу из стандарта я понимаю именно так, как я выше описал, продемонстрировав на примере. Однако компилятор MS VC++ 2010 считает очевидно по-другому, так как он находит имя дружественной функции до его объявления. Приведу пример [pre2]#include "stdafx.h" #include <iostream> void f() { std::cout << "::f()\n"; } namespace N1 { struct A { friend void f(); }; void g() { f(); } void f() { std::cout << "N1::f()\n"; } } int _tmain(int argc, _TCHAR* argv[]) { N1::g(); }[/pre2] Я рассуждаю так, опираясь на цитату, приведенную из стандарта, что при определении функции g() компилятор должен видеть только функцию f() из глобального пространтсва имен, так как объявление дружественной функции f() в пространстве имен N1 еще не видно. Однако вопреки моим ожиданиям при выполнении программы на консоль выводится текст N1::f() вместо, как я полагал, ::f() Поэтому возникает естественный вопрос, то ли я неправильно понимаю данное место в стандарте, то ли компилятор MS VC++ 2010 содержит баг. Было бы любопытно узнать, как другие компиляторы ведут себя в подобной ситуации, и какой получается результат работы примера. Я также сообщил о своем мнении, что это баг, по обратной связи с Microsoft. Поэтому с нетерпением жду, что они напишут в ответ. А пока ответ от Microsoft не пришел, то можно обсудить, у кого какие соображения или контраргументы есть по этому вопросу.

Ответов - 8

PSP: Сыроежка пишет: in that namespace scope (either before or after the class definition granting friendship) Сишник я, мягко говоря, слабый... Но, следуя тексту, что-то про "до или после"... Я понимаю это так, что в пространстве имен N1 имя f() объявлено, а значит видно во всем пространстве.

Сыроежка: PSP пишет: Я понимаю это так, что в пространстве имен N1 имя f() объявлено, а значит видно во всем пространстве. В этом и состоит главная проблема С++, то есть в правильном понимании и интерпретации того, что написано в стандарте. И даже сами разработчики стандарта часто путаются. Я сначала приведу весь параграф стандарта, чтобы можно было получить более полное представление о вопросе. 3 Every name first declared in a namespace is a member of that namespace. If a friend declaration in a nonlocal class first declares a class or function95 the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace. [ Note: The other forms of friend 95) this implies that the name of the class or function is unqualified. § 7.3.1.2 161 declarations cannot declare a new member of the innermost enclosing namespace and thus follow the usual lookup rules. —end note ] [ Example: // Assume f and g have not yet been defined. void h(int); template <class T> void f2(T); namespace A { class X { friend void f(X); // A::f(X) is a friend class Y { friend void g(); // A::g is a friend friend void h(int); // A::h is a friend // ::h not considered friend void f2<>(int); // ::f2<>(int) is a friend }; }; // A::f, A::g and A::h are not visible here X x; void g() { f(x); } // definition of A::g void f(X) { /* ... */} // definition of A::f void h(int) { /* ... */ } // definition of A::h // A::f, A::g and A::h are visible here and known to be friends } using A::x; void h() { A::f(x); A::X::f(x); // error: f is not a member of A::X A::X::Y::g(); // error: g is not a member of A::X::Y } —end example ] Я руководствовался этим примером, который приведен в стандарте. То есть в комментариях к примеру написано, ннапример, // A::f, A::g and A::h are not visible here, поэтому я воспринял это буквально. Но вы посеяли зерна сомнения, выделив место из стандарта про "до и после". Поэтому было бы очень любопытно узнать, как ведут себя другие компиляторы!

Сыроежка: Теперь начинается самое интересное.Microsoft не хочет признавать ошибку компилятора. Я получил следующий ответ: Posted by Microsoft on 1/5/2012 at 2:28 PM Hi: the compiler is behaving correctly in this case. In section 7.3.1.2/p3 of the C++ Standard it states: "If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace." Jonathan Caves Visual C++ Compiler Team В котором просто ссылаются на стандарт, что дружественная функция является членом текущего пространства имен. Такое впечатление, что меня не поняли, то есть либо я чего-то не понимаю, то ли они не понимают Поэтому продолжаем бодаться с Microsoft. Я им отправил комментарий на их ответ Posted by Сыроежка on 1/6/2012 at 3:45 AM But further in the same paragraph the following is written "The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship)." And below this text there is an example in which there is statement // A::f, A::g and A::h are not visible here. However due to the example I presented in my feedback the compiler finds the name of the friend before the declaration of the friend in the scope. Интересно, что они напишут в следующий раз. Не хотелось бы оставлять этот вопрос невыясненным для себя, иначе непонимание так и останется.


Dima : Сыроежка пишет: Интересно, что они напишут в следующий раз. Напиши им на русском. Уверен там есть наши !

Сыроежка: Dima пишет: Напиши им на русском. Уверен там есть наши ! Проблема в том, что это общий сайт для всех сообщений о возможных ошибках, которые они принимают к рассмотрению. То есть читать эти сообщения могут все. А английский - это язык межинтернационального общения программистов. Но если они так и не поймут, чего я добиваюсь, то придется им сказать пару слов по русски!

Сыроежка: Получил я ответ на свое второе сообщение по этому вопросу, но быстрота ответа вызвало у меня сомнения, что поднятый мною воопрос рассматривался достаточно серьезно. Но сначала ответ от Microosoft. Posted by Microsoft on 06.01.2012 at 8:20 Ah yes ... now I see what is going: this is indeed a bug in Visual C++. Unfortunately it is unlikely that we'll be able to fix this for the upcoming release - but we will keep the bug in our database and look at it during the development of a a future release. Sorry for the confusion and thanks for correcting me. Jonathan Caves Visual C++ Compiler Team А сомнения у меня возникли потому, что после первого отказа признать, что это баг VC++ 2010, я стал задумываться над этим вопросом и что-то смутно припоминать, что в главе 3 стандарта С++, посвященной поиску имен, что-то говорилось о невидимых именах и дружественных функциях. Поэтому надо будет внимательно почитать главу 3 стандарта. Может быть действительно Microsoft в первом ответе был прав, а второй ответ от них оказался несколько поспешным?

Сыроежка: Я нашел еще одно место в стандарте С++, которое подтвержает мое утверждение о баге компилятора. В разделе 3.4.1 "Unqualified name lookup" в параграфе 3 приводится следующий пример: [pre2]typedef int f; namespace N { struct A { friend void f(A &); operator int(); void g(A a) { int i = f(a); // f is the typedef, not the friend // function: equivalent to int(a) } }; }[/pre2] То есть согласно стандарту в функции g будет вызвана не дружественная функция f, а оператор преобразования типа, так как имя f будет синонимом типа int. Однако данный код не компилируется MS VC++ 2010, а выдается соообщение об ошибке "error C2440: инициализация: невозможно преобразовать "void" в "int" Выражение, имеющее тип void, нельзя преобразовать в другой тип" То есть компилятор считает, что в этом месте вызывается дружественнная функция.

Сыроежка: Чтобы наглядно увидеть, насколько разные компиляторы соответсвуют стандарту, а, следовательно, насколько важно знать программисту стандарт С/С++, чтобы быть уверенным, что имеет место в конкретной ситуации - то ли ваш код некорректный, то ли компилятор не соответсвует стандарту - можно в режиме он-лайн попробвывать выполнить соответсвующий пример кода. [pre2]#include <iostream> void f() { std::cout << "::f()\n"; } namespace N1 { struct A { friend void f(); }; void g() { f(); } void f() { std::cout << "N1::f()\n"; } } int main() { N1::g(); }[/pre2] Как уже было сказано MS VC++ 2010, а также старый компилятор Borland C++ Builder 5.0 (у кого есть компилятор Borland для запуска из командной строки, моогут также проверить работу компилятора с этим кодом) выводят на консоль текст N1::f(). Но если вы этот пример запустите для он-лайн компилятора по ссылке по ссылке , то вы получите уже другой результат, соответствующий стандарту, а именно ::f() Можете это проделать, скопирово текст примера в окно редактора этого он-лайнового компилятора.



полная версия страницы