Сообщение Шаблонные друзья: шаблонный метод шаблонного класса от 18.11.2022 2:39
Изменено 18.11.2022 3:43 Андрей Тарасевич
Шаблонные друзья: шаблонный метод шаблонного класса
Вариант 1
Наивная попытка объявить специализацию шаблонного метода специализации одного шаблонного класса другом другого шаблонного класса:
Обратите внимание, что во friend-объявлении я хочу явно указать параметр для шаблона функции. Возможно ли это? (Если я уберу `<T>` после `foo`, то все вопросы отпадут.)
Clang вплоть до версии 6 спокойно проглатывал такое объявление друга. GCC же всегда ругался:
Однако более поздние версии Clang тоже начали ругаться на такой вариант
Почесав немного репу, говорим себе: "Ага! Это же вложенный шаблон в шаблоне! А не попробовать ли нам добавить тут `template`?".
Вариант 2
На этом этапе GCC молча принимает такие объявления, в то время как Clang продолжает жаловаться
Радость с GCC тоже длится недолго: достаточно инстанцировать `B`
как GCC тут же выдает сообщение несколько иного рода
на ту же строчку. Странное сообщение, но и так бывает.
То есть и так тоже не получается.
Посему вопрос: а как правильно? Существует ли вообще возможность такого объявления друга и правильный синтаксис для него?
В данном примере возможность, разумеется, существует. Как я заметил выше, достаточно убрать явное указание шаблонного аргумента для `foo`, и все сразу заработает
В таком вариант компилятор дедуцирует правильную специализацию шаблона `foo` по типу аргумента. Но что бы мы делали, если бы у `foo` не было явных аргументов?
Явное указание аргумента работает без проблем для отдельностящих шаблонных функций и шаблонных методов обычных (нешаблонных) классов. Проблема возникает именно в ситуации "шаблон в шаблоне". Возможно ли это?
Наивная попытка объявить специализацию шаблонного метода специализации одного шаблонного класса другом другого шаблонного класса:
template <typename T> struct A
{
template <typename U> void foo(U u);
};
template <typename T> class B
{
friend void A<T>::foo<T>(T);
};
Обратите внимание, что во friend-объявлении я хочу явно указать параметр для шаблона функции. Возможно ли это? (Если я уберу `<T>` после `foo`, то все вопросы отпадут.)
Clang вплоть до версии 6 спокойно проглатывал такое объявление друга. GCC же всегда ругался:
error: invalid use of qualified-name 'A<T>::foo'
8 | friend void A<T>::foo<T>(T);
Однако более поздние версии Clang тоже начали ругаться на такой вариант
error: no candidate function template was found for dependent friend function template specialization
friend void A<T>::foo<T>(T);
^
Почесав немного репу, говорим себе: "Ага! Это же вложенный шаблон в шаблоне! А не попробовать ли нам добавить тут `template`?".
Вариант 2
template <typename T> struct A
{
template <typename U> void foo(U u);
};
template <typename T> class B
{
friend void A<T>::template foo<T>(T);
};
На этом этапе GCC молча принимает такие объявления, в то время как Clang продолжает жаловаться
error: 'template' keyword not permitted here
friend void A<T>::template foo<T>(T);
Радость с GCC тоже длится недолго: достаточно инстанцировать `B`
int main()
{
B<int> b;
}
как GCC тут же выдает сообщение несколько иного рода
error: 'foo' was not declared in this scope
8 | friend void A<T>::template foo<T>(T);
| ^~~~
на ту же строчку. Странное сообщение, но и так бывает.
То есть и так тоже не получается.
Посему вопрос: а как правильно? Существует ли вообще возможность такого объявления друга и правильный синтаксис для него?
В данном примере возможность, разумеется, существует. Как я заметил выше, достаточно убрать явное указание шаблонного аргумента для `foo`, и все сразу заработает
#include <iostream>
template <typename T> struct A
{
template <typename U> void foo(U u);
};
template <typename T> class B
{
friend void A<T>::foo(T);
int b;
};
template <typename T> template <typename U>
void A<T>::foo(U u)
{
std::cout << "Hello World" << std::endl;
B<int> b;
b.b = 42;
}
int main()
{
A<int>().foo(42); // OK
//A<int>().foo(2.0); // ERROR
//A<double>().foo(42); // ERROR
}
В таком вариант компилятор дедуцирует правильную специализацию шаблона `foo` по типу аргумента. Но что бы мы делали, если бы у `foo` не было явных аргументов?
Явное указание аргумента работает без проблем для отдельностящих шаблонных функций и шаблонных методов обычных (нешаблонных) классов. Проблема возникает именно в ситуации "шаблон в шаблоне". Возможно ли это?
Шаблонные друзья: шаблонный метод шаблонного класса
Вариант 1
Наивная попытка объявить специализацию шаблонного метода специализации одного шаблонного класса другом другого шаблонного класса:
Обратите внимание, что во friend-объявлении я хочу явно указать параметр для шаблона функции. Возможно ли это? (Если я уберу `<T>` после `foo`, то все вопросы отпадут.)
Clang вплоть до версии 6 спокойно проглатывал такое объявление друга. GCC же всегда ругался:
Однако более поздние версии Clang тоже начали ругаться на такой вариант
Почесав немного репу, говорим себе: "Ага! Это же вложенный шаблон в шаблоне! А не попробовать ли нам добавить тут `template`?".
Вариант 2
На этом этапе GCC молча принимает такие объявления, в то время как Clang продолжает жаловаться
Радость с GCC тоже длится недолго: достаточно инстанцировать `B`
как GCC тут же выдает сообщение несколько иного рода
на ту же строчку. Странное сообщение, но и так бывает.
То есть и так тоже не получается.
Посему вопрос: а как правильно? Существует ли вообще возможность такого объявления друга и правильный синтаксис для него?
В данном примере возможность, разумеется, существует. Как я заметил выше, достаточно убрать явное указание шаблонного аргумента для `foo`, и все сразу заработает
В таком вариант компилятор дедуцирует правильную специализацию шаблона `foo` по типу аргумента. Но что бы мы делали, если бы у `foo` не было явных аргументов?
Явное указание аргумента работает без проблем для отдельностящих шаблонных функций и шаблонных методов обычных (нешаблонных) классов. Проблема возникает именно в ситуации "шаблон в шаблоне". Возможно ли это?
Наивная попытка объявить специализацию шаблонного метода специализации одного шаблонного класса другом другого шаблонного класса:
template <typename T> struct A
{
template <typename U> void foo(U u);
};
template <typename T> class B
{
friend void A<T>::foo<T>(T);
};
Обратите внимание, что во friend-объявлении я хочу явно указать параметр для шаблона функции. Возможно ли это? (Если я уберу `<T>` после `foo`, то все вопросы отпадут.)
Clang вплоть до версии 6 спокойно проглатывал такое объявление друга. GCC же всегда ругался:
error: invalid use of qualified-name 'A<T>::foo'
8 | friend void A<T>::foo<T>(T);
Однако более поздние версии Clang тоже начали ругаться на такой вариант
error: no candidate function template was found for dependent friend function template specialization
friend void A<T>::foo<T>(T);
^
Почесав немного репу, говорим себе: "Ага! Это же вложенный шаблон в шаблоне! А не попробовать ли нам добавить тут `template`?".
Вариант 2
template <typename T> struct A
{
template <typename U> void foo(U u);
};
template <typename T> class B
{
friend void A<T>::template foo<T>(T);
};
На этом этапе GCC молча принимает такие объявления, в то время как Clang продолжает жаловаться
error: 'template' keyword not permitted here
friend void A<T>::template foo<T>(T);
Радость с GCC тоже длится недолго: достаточно инстанцировать `B`
int main()
{
B<int> b;
}
как GCC тут же выдает сообщение несколько иного рода
error: 'foo' was not declared in this scope
8 | friend void A<T>::template foo<T>(T);
| ^~~~
на ту же строчку. Странное сообщение, но и так бывает.
То есть и так тоже не получается.
Посему вопрос: а как правильно? Существует ли вообще возможность такого объявления друга и правильный синтаксис для него?
В данном примере возможность, разумеется, существует. Как я заметил выше, достаточно убрать явное указание шаблонного аргумента для `foo`, и все сразу заработает
#include <iostream>
template <typename T> struct A
{
template <typename U> void foo(U u);
};
template <typename T> class B
{
friend void A<T>::foo(T);
int b;
};
template <typename T> template <typename U>
void A<T>::foo(U u)
{
std::cout << "Hello World" << std::endl;
B<int> b;
b.b = 42;
}
int main()
{
A<int>().foo(42); // OK
//A<int>().foo(2.0); // ERROR: нет доступа к `B<int>::b`
//A<double>().foo(42); // ERROR: нет доступа к `B<int>::b`
}
В таком вариант компилятор дедуцирует правильную специализацию шаблона `foo` по типу аргумента. Но что бы мы делали, если бы у `foo` не было явных аргументов?
Явное указание аргумента работает без проблем для отдельностящих шаблонных функций и шаблонных методов обычных (нешаблонных) классов. Проблема возникает именно в ситуации "шаблон в шаблоне". Возможно ли это?