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

C++

Войти
Регистрация
Восстановить пароль
 
 
ASCII
90 / 62 / 10
Регистрация: 15.12.2013
Сообщений: 399
Завершенные тесты: 2
#1

Friend declaration construction - C++

16.07.2016, 04:27. Просмотров 1005. Ответов 36
Метки нет (Все метки)

Читаю 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  
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
16.07.2016, 04:27
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Friend declaration construction (C++):

friend указатель - C++
Есть ли что-то типа этого в стандарте: class A { protected: double a; public: typedef double (*td) (double x); td a; ...

Friend функции, определенные в теле класса - C++
Всем привет! Непонятна логика, которой руководствуются компиляторы. Рассмотрим код: #include &lt;iostream&gt; struct A{...

Ошибки "Declaration syntax error" и "Multiple declaration" при компиляции проекта - C++ Builder
Optimalnost_Unit.cpp(6): E2141 Declaration syntax error Optimalnost_Unit.cpp(7): E2238 Multiple declaration for 'Image' ...

Friend и Static - C++ Builder
Здравсвуйте программисты! Изучаю про классы. В теме из книги встречаются постоянно операторы friend и static . Подскажите пожалуйста,как...

Некомпилируется при объявлении friend функции - C++ Builder
Всем доброго времени суток! Может кто-нибудь объяснить почему данный код компилится: #define DEF struct DEF str1 { ...

Declaration terminated incorrectly - C++ Builder
Помогите у меня две ошибки в коде, не могу понять в чем дело: перед 1-ым и 2-ым if-ом...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
ASCII
90 / 62 / 10
Регистрация: 15.12.2013
Сообщений: 399
Завершенные тесты: 2
16.07.2016, 17:11  [ТС] #16
Цитата Сообщение от Nosey Посмотреть сообщение
Все это слишком похоже на баг, но вот проблема что этот баг одинаков на "всех" компиляторах .
Как расшифровывать 3-е правило я не понимаю.
Я полагаю, что возможно это какой-то дефект в стандарте, которому следуют все компиляторы

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

если имя функции-друга полное,
никаких не шаблонных версий не было найдено,
то компилятор должен попытаться вывести специализацию шаблона.
avgoor
885 / 520 / 112
Регистрация: 05.12.2015
Сообщений: 1,465
16.07.2016, 17:23 #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
90 / 62 / 10
Регистрация: 15.12.2013
Сообщений: 399
Завершенные тесты: 2
16.07.2016, 17:33  [ТС] #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;
}
Nosey
1347 / 398 / 107
Регистрация: 22.10.2014
Сообщений: 862
Завершенные тесты: 2
16.07.2016, 17:45 #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.
avgoor
885 / 520 / 112
Регистрация: 05.12.2015
Сообщений: 1,465
16.07.2016, 18:09 #21
Цитата Сообщение от Nosey Посмотреть сообщение
Ваш приведенный код попадает под первое правило, ибо
Тьфу, блин, действительно. Разница там между deducted и non-deducted.
Сейчас ухожу на пьянку. Вечером, если буду в состоянии, продемонстрирую разницу примером. Ну, или завтра, если буду не в состоянии.
Nosey
16.07.2016, 18:44
  #22

Не по теме:

Цитата Сообщение от avgoor Посмотреть сообщение
продемонстрирую разницу примером
Лучше ссылочкой на стандарт, это будет вах какая конфетка.

avgoor
885 / 520 / 112
Регистрация: 05.12.2015
Сообщений: 1,465
17.07.2016, 16:29 #23
Я протрезвел, продолжим
Цитата Сообщение от Nosey Посмотреть сообщение
Лучше ссылочкой на стандарт, это будет вах какая конфетка.
Так вы ж сами ее привели! Впрочем, еще одна понадобится:
14.2 Names of template specializations
1 A template specialization (14.7) can be referred to by a template-id:
simple-template-id:
template-name<template-argument-list opt>
template-id:
simple-template-id
operator-function-id<template-argument-list opt>
...
Сперва приведу код:
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
class A;
 
namespace Foo
{
    template<class T> void foo(A& a, T l, T r);
    void foo(A& a, int l, int r);
}
 
class A
{
    int a;
    friend void Foo::foo<int>(A&, int, int); // Friend declaration 1
    friend void Foo::foo(A&, int, int); // Friend declaration 2
};
 
namespace Foo
{
    template<class T>
    void foo(A& a, T l, T r)
    {
        a.a = l + r;
    }
    void foo(A& a, int l, int r)
    {
        a.a = l + r;
    }
}
 
int main()
{
    A a;
    Foo::foo(a, 1, 2); // Call 1
    Foo::foo<int>(a, 1, 2); // Call 2
}
Т.е. пункт 1:
— 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
относится к Friend declaration 1 (см. код) т.е. с явным указанием шаблона.

Второй и третий пункты:
— 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,
определяют последовательность, в которой ищутся имена, в случае если указано только имя (Friend declaration 2)
Тогда, если есть нешаблонная функция с таким именем - она друг. Потом ищется шаблон с таким именем. Если и он не найден - имя должно быть неквалифицированным, и оно объявляется/определяется во внешнем неймспейсе.

Соответственно: Friend declaration 2 - относится к void foo(), а если ее закомментировать - будет относится к void foo<int>(). Как-то так.
Nosey
1347 / 398 / 107
Регистрация: 22.10.2014
Сообщений: 862
Завершенные тесты: 2
17.07.2016, 17:39 #24
avgoor, Так-то оно так, но вопрос в следующем коде:

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>
 
class A;
 
namespace Foo
{
    template<class T> void foo(A& a, T l, T r);
}
 
class A
{
    int a;
    friend void Foo::foo(A&, int, int); // По стандарту этот код должен работать, а работает только в студии.
//    friend void Foo::foo<>(A&, int, int); // А работает кросплатформенно только этот.
};
 
namespace Foo
{
    template<>
    void foo<int>(A& a, int l, int r)
    {
        a.a = l + r;
    }
}
 
int main()
{
    A a;
    Foo::foo(a, 1, 2); // Call 1
}
avgoor
885 / 520 / 112
Регистрация: 05.12.2015
Сообщений: 1,465
17.07.2016, 18:37 #25
Цитата Сообщение от Nosey Посмотреть сообщение
По стандарту этот код должен работать, а работает только в студии.
Пичалька
ASCII
90 / 62 / 10
Регистрация: 15.12.2013
Сообщений: 399
Завершенные тесты: 2
17.07.2016, 18:40  [ТС] #26
Nosey, так вроде ссылка на статью с авторством Герба Саттера, которую Вы скинули как раз объясняет эти моменты? )) Разве нет? ) Саттер пишет, что это баг и правило #3 следует избегать. Лучше использовать #1, там где qualified or unqualified template-id указывается.
Nosey
1347 / 398 / 107
Регистрация: 22.10.2014
Сообщений: 862
Завершенные тесты: 2
17.07.2016, 19:09 #27
Цитата Сообщение от ASCII Посмотреть сообщение
Разве нет?
Саттер писал что в 2002 году ни одно правило кросскомпиляторно не работало, теперь первое фурычит, еще 15 лет и 3-е тоже заработает
hoggy
6533 / 2713 / 469
Регистрация: 15.11.2014
Сообщений: 5,992
Завершенные тесты: 1
17.07.2016, 19:24 #28
кароче! вы одолели уже!
напишите код,
который иллюстрирует все три правила.

по мне так, 3-е правило нефига не работает,
причем во всех топовых компиляторах

я понимаю - есть обходной путь.

но блин, это не суть.

суть в том, что бы понять значение всех этих правил.

вот давайте с толком, с расстановкой, и по-русски.
с примерами кода.

а потом модераторы вынесут эту жемчужину в шапку раздела.

это реально интересный момент языка.
Nosey
1347 / 398 / 107
Регистрация: 22.10.2014
Сообщений: 862
Завершенные тесты: 2
17.07.2016, 19:50 #29
Цитата Сообщение от hoggy Посмотреть сообщение
напишите код,
который иллюстрирует все три правила.
Цитата Сообщение от hoggy Посмотреть сообщение
который иллюстрирует все три правила.
You are welcome :
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
 
class A;
 
template<typename T> void f1(A& a, T v);
template<typename T> void f1_1(A& a, T v);
template<typename T> void f3(A& a, T v);
void f2(A& a, int v);
 
class A
{
    int a;
    friend void ::f1<>(A&, int); // первое правило - привязывает к шаблонам
    friend void f1_1<>(A&, int); // также первое правило - привязывает к шаблонам, т.е квалификация не обязательна.
    friend void ::f2(A&, int); // второе правило - привязывает к обычным функциям
    friend void ::f3(A&, int); // третье правило - если (2)-ой вариант не сработал - обязаны попытаться привязать к шаблону. Юзая классическую дедукцию аргументов шаблона.
    friend void f4(A&, int); // четвертое правило - компилятор сам объявит эту функцию.
};
 
template<>
void f1<int>(A& a, int v)
{
    a.a = v;
    std::cout << a.a;
}
 
template<>
void f1_1<int>(A& a, int v)
{
    a.a = v;
    std::cout << a.a;
}
 
void f2(A& a, int v)
{
    a.a = v;
    std::cout << a.a;
}
 
template<>
void f3<int>(A& a, int v)
{
    a.a = v;
    std::cout << a.a;
}
 
void f4(A& a, int v)
{
    a.a = v;
    std::cout << a.a;
}
 
int main()
{
    A a;
    f1(a,10);
    f1_1(a,11);
    f2(a,12);
    f3(a,13);
    f4(a,14);
}
В студии верхний код соберется. В остальных компиляторах мне лень дотошно разбираться и описывать, но вот пока баловался понял, что в gcc - багов по теме дружественности больше, чем просто "не привязывает дружественность"
ASCII
90 / 62 / 10
Регистрация: 15.12.2013
Сообщений: 399
Завершенные тесты: 2
17.07.2016, 20:15  [ТС] #30
Nosey, наверное стоит дополнить, что friend declaration для f3,то есть то самое третье правило, как раз оно и является багом в каких-то компиляторах
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
17.07.2016, 20:15
Привет! Вот еще темы с ответами:

Declaration Syntax Error - C++ Builder
Вечер добрый. При компиляции, выдаёт ошибку Unit1.cpp(93): E2141 Declaration syntax error. В чём именно может быть проблема?...

Ошибка Multiple declaration for - C++ Builder
Всем привет. Ребята проблема такая. Я (конечно не сам мне помогли) к проекту прикрутил сорцы от 7z (для распаковки архивов). Все...

Ошибка: friend declaration declares a non-template function - C++
Всем доброго времени суток! Я корплю над задачкой: нада сделать класс вектор шаблонным, перегрузить операции ввода-вывода, и тд и тп. ...

Почему friend ostrem& operator <<(ostream& outs, const Rational&); - invalid function declaration? - C++
Пытаюсь скомпилировать программу пишет friend ostrem&amp; operator &lt;&lt;(ostream&amp; outs, const Rational&amp;); - invalid function declaration. ...


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

Или воспользуйтесь поиском по форуму:
Yandex
Объявления
17.07.2016, 20:15
Ответ Создать тему
Опции темы

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