Форум программистов, компьютерный форум CyberForum.ru

C++

Войти
Регистрация
Восстановить пароль
 
hoggy
6168 / 2534 / 444
Регистрация: 15.11.2014
Сообщений: 5,611
Завершенные тесты: 1
#1

Friend функции, определенные в теле класса - C++

08.02.2015, 21:12. Просмотров 562. Ответов 10
Метки нет (Все метки)

Всем привет!

Непонятна логика, которой руководствуются компиляторы.

Рассмотрим код:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
 
struct A{ friend A* foo(A* ptr){ std::cout<<"ok";return ptr;}};
 
int main(){
    
    A* ptr;
  
    foo(ptr);      // ok
    foo(nullptr); //  error: ‘foo’ was not declared in this scope
    
   return 0;
}
Почему компилятор не выполнил неявного приведения типов?

И вообще, в чем принципиальное отличие от такого варианта:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
 
struct A;
A* foo(A* ptr);
struct A{ friend A* foo(A* ptr){ std::cout<<"ok";return ptr;}};
 
int main(){
    
    A* ptr;
  
    foo(ptr);      // ok
    foo(nullptr); // ok
    
   return 0;
}
Поведение одинаковое для cl(вижал студиия) / gcc/ clang
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
08.02.2015, 21:12     Friend функции, определенные в теле класса
Посмотрите здесь:

Как правильно использовать friend для доступа к экземпляру класса C++
Friend класса C++
C++ friend и member функции
C++ friend функции не имеют доступа к private элементам класса, почему?
Классы. Программирование алгоритмов с использованием конструктора, деструктора, friend - функции инициализации set() и функции вывода результатов prin C++
C++ Классы. Программирование алгоритмов с использованием конструктора, деструктора, friend - функции инициализации set() и функции вывода результатов pri
Дружественные (friend) функции C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Avazart
7066 / 5243 / 263
Регистрация: 10.12.2010
Сообщений: 23,070
Записей в блоге: 17
08.02.2015, 22:19     Friend функции, определенные в теле класса #2
А каким боком тут вообще friend ?
DiffEreD
1427 / 764 / 95
Регистрация: 21.06.2011
Сообщений: 1,740
Записей в блоге: 2
09.02.2015, 14:45     Friend функции, определенные в теле класса #3
Тут воде проблема в правилах ADL. foo будет видна только если фактические параметры у нее будут из области видимости структуры.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct B {
   friend void bar(const std::string &) {}
   friend void bar(const std::string &, B*) {}
   friend void foo(B *) {}
};
 
int main()
{
   B* b;
   foo(b);
   //bar("Fail"); //'bar' was not declared in this scope
   bar("Ok", b);
 
}
hoggy
6168 / 2534 / 444
Регистрация: 15.11.2014
Сообщений: 5,611
Завершенные тесты: 1
09.02.2015, 15:18  [ТС]     Friend функции, определенные в теле класса #4
Цитата Сообщение от DiffEreD Посмотреть сообщение
Тут воде проблема в правилах ADL. foo будет видна только если фактические параметры у нее будут из области видимости структуры.
Это мне как бы итак понятно.
Мне не понятно, что за чертовщина, и почему оно так?

В каком скоупе по факту оказывается функция-друг, которая определена в теле класса?


11.3 Friends

6 A function can be defined in a friend declaration of a class if and only if the class is a non-local class (9.8),
the function name is unqualified, and the function has namespace scope. [ Example:
class M {
friend void f() { } // definition of global f, a friend of M,
// not the definition of a member function
};
— end example ]
По идее такая функция друг должна быть видна из того же скоупа,
в котором был объявлен сам класс.

Тогда не понятно, почему до неё иначе,
чем при помощи Argument-dependent name lookup не дотянуться???

Там ниже:

7 Such a function is implicitly inline. A friend function defined in a class is in the (lexical) scope of the
class in which it is defined. A friend function defined outside the class is not (3.4.1).
Эту фразу я как то вообще не осилил.

Ссылка 3.4.1 описывает правила собственно:

3.4.1 Unqualified name lookup [basic.lookup.unqual]
Там километр корявого текста, суть которого:
неквалифицированное имя ищется в первую очередь среди полных (complete) имен, в ближайшем скоупе.

То бишь локальным именам отдается предпочтение, если они пригодны к использованию.

В данном случае единственное пригодное имя - имя функции, которая была определена в теле класса.

Вопрос: почему она вообще не видна без Argument-dependent name lookup ?

Я может быть чего то не так понял, и она на самом деле реально существует в пространстве имени самого класса, а не в пространстве, где был объявлен класс????

Но если так, то почему она не может быть квалифицирована?
Enno
266 / 169 / 38
Регистрация: 25.08.2014
Сообщений: 1,088
Записей в блоге: 1
11.02.2015, 04:53     Friend функции, определенные в теле класса #5
Цитата Сообщение от DiffEreD Посмотреть сообщение
foo будет видна только если фактические параметры у нее будут из области видимости структуры.
Цитата Сообщение от DiffEreD Посмотреть сообщение
friend void bar(const std::string &) {}
std::string не видна из class B?
Croessmah
Модератор
Эксперт CЭксперт С++
12890 / 7276 / 811
Регистрация: 27.09.2012
Сообщений: 17,975
Записей в блоге: 2
Завершенные тесты: 1
11.02.2015, 09:36     Friend функции, определенные в теле класса #6
Цитата Сообщение от Enno Посмотреть сообщение
std::string не видна из class B?
Причем тут std::string? Речь идет о Argument-dependent name lookup
Enno
266 / 169 / 38
Регистрация: 25.08.2014
Сообщений: 1,088
Записей в блоге: 1
11.02.2015, 10:41     Friend функции, определенные в теле класса #7
Цитата Сообщение от Croessmah Посмотреть сообщение
Причем тут std::string? Речь идет о Argument-dependent name lookup
Окей, тогда что такое "фактические параметры" в приведённом примере?
hoggy
6168 / 2534 / 444
Регистрация: 15.11.2014
Сообщений: 5,611
Завершенные тесты: 1
11.02.2015, 14:07  [ТС]     Friend функции, определенные в теле класса #8
Цитата Сообщение от Enno Посмотреть сообщение
Окей, тогда что такое "фактические параметры" в приведённом примере?
"Формальные параметры функции", или просто "параметры функции" - это типы,
которые были указаны в прототипе функции.
Типы данных которые принимает функция, если угодно.

"Фактические параметры" - это аргументы с которыми функция была вызвана.

----------------------------------------------------------------------------------------------------------

В приведенном примере имеет место быть Argument-dependent name lookup

Суть:

Взгляните на этот пример:

C++
1
2
3
4
5
6
7
8
using namespace std;
 
int main()
{
    std::cout << "Hello, world!\n";
    int a=1,b=2;
    swap(a,b);  //<--- не квалифицирована. Не указанно, что она std::
}
Тем не менее, вызовется именно std::swap, просто потому, что никаких других в наличии не имеется.

Теперь такой пример:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
 
namespace example
{
    struct A{ int v; };
    
    void swap(A& l, A& r) //<--- собственность example
    {
        std::cout<<"version: example\n";
        ::std::swap(l.v, r.v); //обратите внимание: 
          //квалифицированный вызов однозначно дает понять, 
          //что на самом деле нужно вызвать
    }
};
 
template <class T> void Swap(T& a1, T& a2) 
{
    using std::swap;
    swap(a1, a2);       //<--- какая версия будет использвана? std:: или example::
}
 
using namespace example;
 
int main()
{
    std::cout << "Hello, world!\n";
    
    A a,b;
    Swap(a,b);
}
ответ:
http://rextester.com/YWMU22074

Здесь мы видим так называемый Argument-dependent name lookup в действии.

В первую очередь компилятор отдает предпочтение тем функциям,
которые объявлены в той же пространстве имен, где и их аргументы.

Поскольку A объявлено в example, то и swap он в первую очередь тоже ищет в example.

--------------

Этот пример иллюстрирует: что будет, если не находит:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
 
namespace example
{
    struct A{ int v; };
};
 
void swap(example::A& l, example::A& r) //<--- глобальная
{
    std::cout<<"version: global\n";
    ::std::swap(l.v, r.v); 
}
 
template <class T> void Swap(T& a1, T& a2) 
{
    using std::swap;
    swap(a1, a2);       //<--- какая версия будет использвана? std:: или глобальная?
}
 
using namespace example;
 
int main()
{
    std::cout << "Hello, world!\n";
    
    A a,b;
    Swap(a,b);
}
http://rextester.com/AHD74216

Ответ: выбрана будет версия std.

Это связанно с двумя вещами:

1. Argument-dependent name lookup провалился.
В пространстве имен example не оказалось функции swap.

2. Это связанно с использованием using std::swap;
Данная инструкция объединила пространство имен скоупа функции с пространством имен std.

Как будто бы имя std::swap объявлено в скоупе самой функции.
И получилось, что именно это имя оказалось "ближайшим пригодным к использованию",
а вовсе не глобальная, которая находится за пределами текущего скоупа.

-------------------------------------

Ну и напоследок рассмотрим такой пример:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
 
namespace example
{
    struct A{ int v; };
};
 
using std::swap;  //<--- объединили глобальное пространство с std::swap
 
 
// теперь в глобальном пространстве существуют одновременно
// и эта функция, и std::swap
void swap(example::A& l, example::A& r) 
{
    std::cout<<"version: global\n";
    ::std::swap(l.v, r.v); 
}
 
template <class T> void Swap(T& a1, T& a2) 
{
    swap(a1, a2);  //<--- какая версия будет использована? std:: или глобальная?
}
 
using namespace example;
 
int main()
{
    std::cout << "Hello, world!\n";
    
    A a,b;
    Swap(a,b);
}
http://rextester.com/EUG41903

Выбирается глобальное, потому что на конкурсе претендентов предпочтение всегда отдается тем функциям,
чьи параметры совпадают с аргументами вызова,
и при этом их не нужно инстанцировать из шаблона.
Enno
266 / 169 / 38
Регистрация: 25.08.2014
Сообщений: 1,088
Записей в блоге: 1
11.02.2015, 14:44     Friend функции, определенные в теле класса #9
Цитата Сообщение от hoggy Посмотреть сообщение
В первую очередь компилятор отдает предпочтение тем функциям,
которые объявлены в той же пространстве имен, где и их аргументы.
Может не "аргументы", а "типы аргументов"?
ForEveR
Модератор
Эксперт С++
7958 / 4720 / 319
Регистрация: 24.06.2010
Сообщений: 10,525
Завершенные тесты: 3
11.02.2015, 14:45     Friend функции, определенные в теле класса #10
Был похожий вопрос на стеке: http://stackoverflow.com/questions/2...76675#27376675
В комменте к моему ответу дали пункт стандарта, который описывает данное поведение.

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-
local class first declares a class or function 95 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).
Поэтому такой пример например вполне себе заработает.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
 
struct A{ friend A* foo(A* ptr){ std::cout<<"ok";return ptr;}};
 
A* foo(A*);
 
int main(){
    
    A* ptr;
  
    foo(ptr);      // ok
    foo(nullptr); //  error: ‘foo’ was not declared in this scope
    
   return 0;
}
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
11.02.2015, 15:28     Friend функции, определенные в теле класса
Еще ссылки по теме:

C++ Не могу обратиться к Privat члену класса из ф-и Friend. Почему?
C++ Friend-функции
Перегрузка операторов, friend или нет friend? C++
Не работают friend функции C++
C++ Как объявить friend функцию класса

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
hoggy
6168 / 2534 / 444
Регистрация: 15.11.2014
Сообщений: 5,611
Завершенные тесты: 1
11.02.2015, 15:28  [ТС]     Friend функции, определенные в теле класса #11
Цитата Сообщение от Enno Посмотреть сообщение
Может не "аргументы", а "типы аргументов"?
да.

Добавлено через 19 минут
Цитата Сообщение от ForEveR Посмотреть сообщение
Поэтому такой пример например вполне себе заработает.
Наконец то тяжелая артиллерия подтянулась.

Спасибо)

Document Number: N4296
Date: 2014-11-19
Revises: N4140
Reply to: Richard Smith
Google Inc
cxxeditor@gmail.com



7.3.1.2 Namespace member definitions [namespace.memdef]

If a friend declaration in a non-local class first declares a class, function, class template or function template97
the friend is a member of the innermost enclosing namespace. The friend declaration does not by
itself make the name visible to unqualified lookup (3.4.1) or qualified lookup (3.4.3). [ Note: The name of
the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either
before or after the class definition granting friendship). — end note ]

Резюмируя:
Функция-друг определяется в том же скоупе, где и класс.
Однако, пока программист сам явно ручками не пропишет объявление функции,
её имя (прототип) в этом скоупе будет не видимым.

В этом случае получается, что определение существует, но без объявления...
----------------------------------

По-моему тут есть какое то противоречие здравому смыслу.

Я точно все правильно понял?
Yandex
Объявления
11.02.2015, 15:28     Friend функции, определенные в теле класса
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru