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

Friend declaration construction - C++

Войти
Регистрация
Восстановить пароль
 
 
ASCII
 Аватар для ASCII
84 / 56 / 9
Регистрация: 15.12.2013
Сообщений: 376
Завершенные тесты: 2
16.07.2016, 04:27     Friend declaration construction #1
Читаю C++ Templates. The Complete Guide. Вандервурд, Джоссатис
В одной из глав речь идет об объявлениях дружественных конструкций.

Основная идея такова:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
template <typename T1, typename T2>
void combine(T1, T2);
 
class Mixer
{
    friend void combine<>(int, int);
    friend void combine<int, int>(int, int);
    friend void combine<char>(char, int);
 
    // errors
    friend void combine<char>(char&, int);
    friend void combine<>(long, long) {  }
};
Можно объявить дружественной шаблон функции. Если можно вывести аргументы шаблона, угловые скобки могут быть пустыми.
Определение специализации шаблона запрещено.

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

1) Если имя не полное (без указания пространств имен или класса), то такое имя может ссылаться только на обычную функцию.
При этом, если в точке объявления функции в качестве дружественной она еще не была объявлена, то дружественная конструкция,
ее первое объявление, в таком случае оно может быть и определением.



2) Если имя полное (содержит :: ), то оно должно ссылаться на ранее объявленную функцию или шаблон функции.
Предпочтение отдается функции перед шаблоном функции. Не может быть определения.


Дается разъясняющий пример:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
void multiply(void*);
 
template <typename T>
void multiply(T);
 
class Pride
{
public:
    friend void multiply(int) { }           /* имя не полное, может быть определением */
    friend void ::multiply(void*);        /* имя полное, ссылается на обычную функцию */
    friend void ::multiply(int);            /* имя полное, ссылается на шаблон функции для типа int */
    friend void ::multiply<double*>(double*); /* также могут быть и угловые скобки */
};
Проблема возникает для:

C++
1
    friend void ::multiply(int);  /* имя полное, ссылается на шаблон функции для типа int */
Если переписать этот пример вот так, для проверки дружественности специализации шаблона функции
multiply<int> классу Mixer:

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
void multiply(void*);
 
template <typename T>
void multiply(T);
 
class Pride
{
    int p;
public:
    friend void multiply(int) { }
    friend void ::multiply(void*);
    friend void ::multiply(int);
    friend void ::multiply<double*>(double*);
};
 
template <>
void multiply(double*)
{
    Pride pr;
    pr.p = 0; /* тут все нормально */
}
 
template <>
void multiply(int)
{
    Pride pr;
    pr.p = 0; /* а тут ошибка, то есть эта специализация не является дружественной */
}
Почему так?
Миниатюры
Friend declaration construction  
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
hoggy
5607 / 2237 / 411
Регистрация: 15.11.2014
Сообщений: 5,031
Завершенные тесты: 1
16.07.2016, 05:44     Friend declaration construction #2
рассмотрим код:
http://rextester.com/ZFGA41569

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
33
34
35
36
37
38
39
40
#include <iostream>
 
 
void multiply(void*);
 
template <typename T>
void multiply(T);
 
class Pride
{
    int p;
public:
    friend void multiply(int) { }
    friend void ::multiply(void*);
    friend void ::multiply(int);
    friend void ::multiply<double*>(double*);
};
 
template <>
void multiply(double*)
{
    Pride pr;
    pr.p = 0; /* тут все нормально */
    (void)pr;
    
}
 
template <>
void multiply(int)
{
    Pride pr;
    pr.p = 0; /* а тут ошибка, то есть эта специализация не является дружественной */
    (void)pr;
}
 
 
int main()
{
    std::cout << "Hello, world!\n";
}
главное на что нужно обратить внимание:
выхлоп компилятора:
Код
Error(s):
source_file.cpp:17:31: warning: ‘void multiply(int)’ is already a friend of class ‘Pride’
     friend void ::multiply(int);
                               ^
source_file.cpp: In function ‘void multiply(T) [with T = int]’:
source_file.cpp:13:9: error: ‘int Pride::p’ is private
     int p;
         ^
source_file.cpp:34:8: error: within this context
     pr.p = 0; /* а тут ошибка, то есть эта специализация не является дружественной */
        ^
что произошло?

в строке:
C++
1
friend void multiply(int) { }
в глобальном пространстве возникло определение функции обычной не шаблонной функции,
которая является другом класса.

далее в строке:
C++
1
friend void ::multiply(int);
указание что эта обычная не шаблонная функция - друг.

таким образом получается, что одна и та же функция дважды объявляется другом.
при этом специализация шаблона пролетела мимо кассы.

если написать так:
C++
1
friend void ::multiply<int>(int);
то компилятору явно указывается,
что друг - специализация шаблона под конкретный параметр.
он уже не сможет спутать такой синтаксис с не шаблонной функцией,
и все работает как часы.
ASCII
 Аватар для ASCII
84 / 56 / 9
Регистрация: 15.12.2013
Сообщений: 376
Завершенные тесты: 2
16.07.2016, 14:19  [ТС]     Friend declaration construction #3
hoggy, я убрал определение обычной функции, но оставил
C++
1
friend void ::multiply(int);
Тем не менее ошибка. То есть можно сделать вывод, что в книге ошибка? В частности второе правило?
hoggy
5607 / 2237 / 411
Регистрация: 15.11.2014
Сообщений: 5,031
Завершенные тесты: 1
16.07.2016, 15:04     Friend declaration construction #4
Цитата Сообщение от ASCII Посмотреть сообщение
То есть можно сделать вывод, что в книге ошибка?
хм...
вопрос интересный.

вот что я нашел в стандарте:
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.
если я правильно все понял,
то дружественные функции не могут быть квалифицированы.
(иметь полное имя)

они всегда определяются в том же пространстве имен,
что и класс, в котором они объявляются.

то есть, тут уже идет какое то расхождение с книгой...

похоже на то, что компилятор встретив
нешаблоное объявление функции-друга,
и воспринимает его, как не шаблонное.
ASCII
 Аватар для ASCII
84 / 56 / 9
Регистрация: 15.12.2013
Сообщений: 376
Завершенные тесты: 2
16.07.2016, 15:26  [ТС]     Friend declaration construction #5
Цитата Сообщение от hoggy Посмотреть сообщение
то дружественные функции не могут быть квалифицированы.
(иметь полное имя)
Вообще никакие?
А разве

C++
1
friend void ::multiply(void*);
Не qualified name?
Надо покопаться в стандарте
Nosey
 Аватар для Nosey
1203 / 370 / 106
Регистрация: 22.10.2014
Сообщений: 810
Завершенные тесты: 2
16.07.2016, 15:35     Friend declaration construction #6
Сообщение было отмечено автором темы, экспертом или модератором как ответ
ASCII, Вместо правил книги, мне кажутся правила стандарта легче воспринимаются:

Цитата Сообщение от 14.5.4 Friends
— if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a
specialization of a function template, otherwise
— if the name of the friend is a qualified-id and a matching non-template function is found in the specified
class or namespace, the friend declaration refers to that function, otherwise,
— if the name of the friend is a qualified-id and a matching function template is found in the speci-
fied class or namespace, the friend declaration refers to the deduced specialization of that function
template (14.8.2.6), otherwise,
— the name shall be an unqualified-id that declares (or redeclares) an ordinary (non-template) function.
Все кроме 3-его правила , я например его не понимаю, и собственно это правило как раз разрешает ваше объявление
C++
1
friend void ::multiply(int);
На практике "это правило" не привязывает дружественность к специализации шаблона, а может привязать к общей шаблонной функции.
Т.е. ваше верхнее объявление привязывает дружественность к
C++
1
2
template <typename T>
void multiply(T) {// where T = int}
И кладет болт на специализацию.
Можно предположить что объявление специализации перед определением класса - решило бы проблему:
C++
1
2
3
4
5
6
7
8
template <typename T>
void multiply(T);
 
template <>
void multiply<int>(int);
// класс
 
// реализация специализации
но такого не происходит

Все это слишком похоже на баг, но вот проблема что этот баг одинаков на "всех" компиляторах .
Как расшифровывать 3-е правило я не понимаю.

Если мы определим дружественность таким образом:
C++
1
friend void ::multiply<>(int);
То мы попадем в первое правило, которое верно отрабатывает на "всех" компиляторах.

Цитата Сообщение от hoggy Посмотреть сообщение
если я правильно все понял,
то дружественные функции не могут быть квалифицированы.
(иметь полное имя)
Вам не кажется что это бред?
Ну и к тому же в стандарте приведен пример рассказывающий что относится к "инлайн" определению функции, аля :
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
 
class Pride
{
    friend void f() {std::cout << "Да, я в глобальном пространстве, и не смотрите где я написана" << std::endl;}
};
 
void f();
 
int main()
{
    f();
}
avgoor
562 / 352 / 83
Регистрация: 05.12.2015
Сообщений: 1,137
16.07.2016, 15:54     Friend declaration construction #7
Цитата Сообщение от hoggy Посмотреть сообщение
если я правильно все понял,
то дружественные функции не могут быть квалифицированы.
Вы не правильно поняли.
Вот так можно:
C++
1
2
3
4
5
6
7
8
9
10
11
class A
{
    int a;
    friend void foo(A& a) { a.a = 1; }
};
 
int main()
{
    A a;
    foo(a);
}
А если foo квалифицировать - то нельзя.
hoggy
5607 / 2237 / 411
Регистрация: 15.11.2014
Сообщений: 5,031
Завершенные тесты: 1
16.07.2016, 16:17     Friend declaration construction #8
Цитата Сообщение от Nosey Посмотреть сообщение
Вам не кажется что это бред?
нет, не кажется.
квалифицированная - значит имеет полное имя с указанием всех спейсов.

http://rextester.com/YOURR59359
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
33
34
35
36
37
38
#include <iostream>
 
template <typename T> void multiply(T);
 
namespace sample{
 
class Pride
{
    int p;
public:
    friend void ::multiply<int>(int);
    friend void ::multiply<double*>(double*);
};
    
}namespace sample    
 
template <>
void multiply(double*)
{
    Pride pr;
    pr.p = 0; /* тут все нормально */
    (void)pr;
    
}
 
template <>
void multiply(int)
{
    Pride pr;
    pr.p = 0; /* а тут ошибка, то есть эта специализация не является дружественной */
    (void)pr;
}
 
 
int main()
{
    std::cout << "Hello, world!\n";
}

Код
Error(s):
source_file.cpp:19:1: error: expected ‘{’ before ‘template’
 template <>
 ^
source_file.cpp:20:22: error: ‘multiply’ is not a template function
 void multiply(double*)
                      ^
source_file.cpp:29:18: error: ‘multiply’ is not a template function
 void multiply(int)
                  ^
source_file.cpp: In function ‘int sample::main()’:
source_file.cpp:40:1: warning: no return statement in function returning non-void [-Wreturn-type]
 }
 ^
source_file.cpp: At global scope:
source_file.cpp:40:1: error: expected ‘}’ at end of input

дружественная функция, определение которой описано в теле класса не имеет к этому никакого отношения

Добавлено через 55 секунд
Цитата Сообщение от avgoor Посмотреть сообщение
Вы не правильно поняли.
Вот так можно:
квалифицированная - значит имеет полное имя с указанием всех спейсов.
дружественная функция,
определение которой описано в теле класса,
не имеет к этому никакого отношения

Добавлено через 1 минуту
получается, что книга не врет.
но почему то не работает ...
avgoor
562 / 352 / 83
Регистрация: 05.12.2015
Сообщений: 1,137
16.07.2016, 16:22     Friend declaration construction #9
Цитата Сообщение от hoggy Посмотреть сообщение
дружественная функция,
определение которой описано в теле класса,
не имеет к этому никакого отношения
Да ну?
Цитата Сообщение от hoggy Посмотреть сообщение
вот что я нашел в стандарте:
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.
если я правильно все понял,
Что такое definition и declaration помните?
hoggy
5607 / 2237 / 411
Регистрация: 15.11.2014
Сообщений: 5,031
Завершенные тесты: 1
16.07.2016, 16:27     Friend declaration construction #10
Цитата Сообщение от avgoor Посмотреть сообщение
Что такое definition и declaration помните?
хотите сказать, что неквалифицированными являются только те,
что определены в теле класса?

ну ок, тогда почему не работает код из #8,
и как заставить его работать?
avgoor
562 / 352 / 83
Регистрация: 05.12.2015
Сообщений: 1,137
16.07.2016, 16:31     Friend declaration construction #11
hoggy, Я хочу сказать что ваша цитата относится к определению функций в объявлении друзей и ни к чему другому.
Nosey
 Аватар для Nosey
1203 / 370 / 106
Регистрация: 22.10.2014
Сообщений: 810
Завершенные тесты: 2
16.07.2016, 16:38     Friend declaration construction #12
Цитата Сообщение от hoggy Посмотреть сообщение
ну ок, тогда почему не работает код из #8,
и как заставить его работать?
Написать этот код на С++ , т.е. посмотреть на код, на ошибки и проставить где надо комментарии и пространства имён :
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
33
34
35
36
37
38
#include <iostream>
 
template <typename T> void multiply(T);
 
namespace sample{
 
class Pride
{
    int p;
public:
    friend void ::multiply<int>(int);
    friend void ::multiply<double*>(double*);
};
    
}//namespace sample    
 
template <>
void multiply(double*)
{
    sample::Pride pr;
    pr.p = 0; /* тут все нормально */
    (void)pr;
    
}
 
template <>
void multiply(int)
{
    sample::Pride pr;
    pr.p = 0; /* а тут ошибка, то есть эта специализация не является дружественной */
    (void)pr;
}
 
 
int main()
{
    std::cout << "Hello, world!\n";
}

Не по теме:

hoggy, Простите, но вы что-то сегодня курили али пили?

ASCII
 Аватар для ASCII
84 / 56 / 9
Регистрация: 15.12.2013
Сообщений: 376
Завершенные тесты: 2
16.07.2016, 17:06  [ТС]     Friend declaration construction #13
Nosey,
Цитата Сообщение от Nosey Посмотреть сообщение
— if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a
specialization of a function template, otherwise
— if the name of the friend is a qualified-id and a matching non-template function is found in the specified
class or namespace, the friend declaration refers to that function, otherwise,
— if the name of the friend is a qualified-id and a matching function template is found in the speci-
fied class or namespace, the friend declaration refers to the deduced specialization of that function
template (14.8.2.6), otherwise,
— the name shall be an unqualified-id that declares (or redeclares) an ordinary (non-template) function.
1) Если имя друга квалифицированный или не квалифицированный template-id ( name_of_function<type1, type2> ), то объявление друга ссылается на специализацию шаблона функции

Тут вроде все понятно, тут говорится про template-id, то есть с указанием угловых скобок

2) Если имя друга квалифицированный идентификатор и соответствует не шаблонной функции в указанном классе или пространстве
имен (как тут правильней перевести?)
, объявление друга ссылается на функцию (обычную)

3) Если имя друга квалифицированный идентификатор и соответствует найденной шаблонной функции в указанном классе или
пространстве имен, объявление друга ссылается на выведенную специализацию шаблона того шаблона функции

4) в противном случае имя должно быть неквалифицированным идентификатором, которое объявляет обычно не шаблонную функцию.

Что это все значит??? Перевести это одно, а вот правильно понять - другое...

Добавлено через 21 минуту
А действительно третее правило не понятно. Говорится, что если квалифицированное имя соответствует шаблону, то дружественность ссылается на специализацию шаблона (ее инстанциированную версию для указанного типа). Однако почему тогда пример не работает? Не пойму чет
Nosey
 Аватар для Nosey
1203 / 370 / 106
Регистрация: 22.10.2014
Сообщений: 810
Завершенные тесты: 2
16.07.2016, 17:07     Friend declaration construction #14
Цитата Сообщение от ASCII Посмотреть сообщение
Если имя друга квалифицированный идентификатор и соответствует не шаблонной функции в указанном классе или пространстве
имен (как тут правильней перевести?), объявление друга ссылается на функцию (обычную)
Вы правильно перевели, разве что при чтении вами выделенного текста "указанном классе или пространстве имен" вы уже забыли что "имя друга квалифицированный". Т.е. если нашлась функция по "указанной квалификации", а это не просто жопой на клавиатуру сели

Самый главный вопрос, что означает в 3-ем пункте "deduced specialization of that function template", а точнее почему компилятор кладет болт на в последующем определенную специализацию. И еще больший вопрос, а почему же он не кладёт болт в первом пункте.
hoggy
5607 / 2237 / 411
Регистрация: 15.11.2014
Сообщений: 5,031
Завершенные тесты: 1
16.07.2016, 17:08     Friend declaration construction #15
Цитата Сообщение от avgoor Посмотреть сообщение
Я хочу сказать что ваша цитата относится к определению функций в объявлении друзей и ни к чему другому.
я уже понял.
спасибо.

Цитата Сообщение от Nosey Посмотреть сообщение
Простите, но вы что-то сегодня курили али пили?
твою ж заногу!
блин...

надо ж было так лопухнуццо
я не спал две ночи уже)
видимо внимание рассеялось
ASCII
 Аватар для ASCII
84 / 56 / 9
Регистрация: 15.12.2013
Сообщений: 376
Завершенные тесты: 2
16.07.2016, 17:11  [ТС]     Friend declaration construction #16
Цитата Сообщение от Nosey Посмотреть сообщение
Все это слишком похоже на баг, но вот проблема что этот баг одинаков на "всех" компиляторах .
Как расшифровывать 3-е правило я не понимаю.
Я полагаю, что возможно это какой-то дефект в стандарте, которому следуют все компиляторы

Добавлено через 1 минуту
Цитата Сообщение от Nosey Посмотреть сообщение
Т.е. если нашлась функция по "указанной квалификации", а это не просто жопой на клавиатуру сели
hoggy
5607 / 2237 / 411
Регистрация: 15.11.2014
Сообщений: 5,031
Завершенные тесты: 1
16.07.2016, 17:14     Friend declaration construction #17
Цитата Сообщение от Nosey Посмотреть сообщение
Самый главный вопрос, что означает в 3-ем пункте "deduced specialization of that function template", а точнее почему компилятор кладет болт на в последующем определенную специализацию. И еще больший вопрос, а почему же он не кладёт болт в первом пункте.
я так понял, что это и есть то самое, про что пишет Джоссатис:

если имя функции-друга полное,
никаких не шаблонных версий не было найдено,
то компилятор должен попытаться вывести специализацию шаблона.
avgoor
562 / 352 / 83
Регистрация: 05.12.2015
Сообщений: 1,137
16.07.2016, 17:23     Friend declaration construction #18
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Nosey, Третье правило означает, что шаблон должен быть ранее декларирован, например:
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
class A;
namespace Foo
{
    template<class T> void foo(A& a, T t); // Необходимо, чтоб сработало третье правило
}
 
class A
{
    int a;
    template<class T>
    friend void Foo::foo(A& a, T t); // для этого
};
 
namespace Foo
{
    template<>
    void foo<int>(A& a, int t)
    {
        a.a = 1;
    }
}
 
 
int main()
{
    A a;
    Foo::foo(a, 1);
}
ASCII
 Аватар для ASCII
84 / 56 / 9
Регистрация: 15.12.2013
Сообщений: 376
Завершенные тесты: 2
16.07.2016, 17:33  [ТС]     Friend declaration construction #19
Цитата Сообщение от avgoor Посмотреть сообщение
Третье правило означает, что шаблон должен быть ранее декларирован
Если qualified-id, то да. Но мне кажется там акцент делается не на этом. Ведь и шаблон функции тоже можно определять
в friend declaration, если это его первое объявление:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A
{
    int a;
    template <typename T>
    friend void changer(T)
    {
        A aobj;
        aobj.a = 0;
    }
};
 
template <typename T>
void changer(T);
 
int main()
{
    changer(0); // фиктивный параметр
    return 0;
}
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
16.07.2016, 17:45     Friend declaration construction
Еще ссылки по теме:

Класс friend C++
friend class C++
Ошибки "Declaration syntax error" и "Multiple declaration" при компиляции проекта C++ Builder
Перегрузка операторов, friend или нет friend? C++
Friend для friend C++

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

Или воспользуйтесь поиском по форуму:
Nosey
 Аватар для Nosey
1203 / 370 / 106
Регистрация: 22.10.2014
Сообщений: 810
Завершенные тесты: 2
16.07.2016, 17:45     Friend declaration construction #20
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Цитата Сообщение от hoggy Посмотреть сообщение
я так понял, что это и есть то самое, про что пишет Джоссатис:
если имя функции-друга полное,
никаких не шаблонных версий не было найдено,
то компилятор должен попытаться вывести специализацию шаблона.
А слона-то я и не приметил. Да, пишет о практической работе.

Но проблема что в стандарте, насколько я могу судить, это не указано, и даже напротив, указано обратное :
Цитата Сообщение от 14.8.2.6 Deducing template arguments from a function declaration
1 In a declaration whose declarator-id refers to a specialization of a function template, template argument
deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done
for explicit instantiations (14.7.2), explicit specializations (14.7.3), and certain friend declarations (14.5.4)
и так навсякий случай добавлю следующее :
Цитата Сообщение от 14.7.3 Explicit specialization
3 ... Such a
declaration may also be a definition. If the declaration is not a definition, the specialization may be defined
later

А также я наврал насчет "всех копиляторов".
В студии следующий код собрался:
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
#include <iostream>
 
template <typename T>
void multiply(T);
 
class Pride
{
    int p;
public:
    friend void ::multiply(int);
};
 
template <>
void multiply(int)
{
    Pride pr;
    pr.p = 10; /* member p is private */
    std::cout << pr.p;
}
 
int main()
{
    multiply(10);
}
Так что всё-таки выходит что это баг gcc и шланга. Годы идут, а ничего не меняется

Добавлено через 5 минут
avgoor, Ваш приведенный код попадает под первое правило, ибо
Цитата Сообщение от avgoor Посмотреть сообщение
template<class T> friend void Foo::foo(A& a, T t); // для этого
это без сомнений qualified template-id.
Yandex
Объявления
16.07.2016, 17:45     Friend declaration construction
Ответ Создать тему
Опции темы

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