template <typename T> struct S
{
friend void foo()
{
std::cout << "Hello World" << std::endl;
}
friend void bar()
{
T t{};
std::cout << t << std::endl;
}
};
Если я попытаюсь инстанцировать такой шаблон для двух разных типов `T` в одной единице трансляции, я сразу получу от компилятора сообщение об ошибке из-за множественного определения функций `foo` и `bar`. Например
int main()
{
S<int> a;
S<double> b;
}
In instantiation of 'struct S<double>':
error: redefinition of 'void foo()'
5 | friend void foo()
| ^~~
main.cpp:10:15: error: redefinition of 'void bar()'
10 | friend void bar()
| ^~~
Это понятно.
Но что произойдет, если я сделаю два разных инстанцирования этого шаблона в разных единицах трансляции?
Обратите внимание, что `foo` и `bar` являются не шаблонными (template), но "шаблонизированными" (templated) inline-функциями. При этом я специально сделал определение `foo` не зависящим от параметра шаблона `T`, а определение `bar` — явно зависящим от параметра шаблона.
Я бы предположил, что определения `foo` в разных единицах трансляции соответствуют всем требованиям, налагаемым на определения inline-функций, то есть с `foo` все в порядке. А вот `bar`, из-за ее зависимости от `T`, нарушает эти требования. Соответственно на `bar` я буду иметь нарушение ODR, скорее всего с "no diagnostic required".
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>>Но что произойдет, если я сделаю два разных инстанцирования этого шаблона в разных единицах трансляции?
NB>а какое linkage у таких функций?
External, вестимо. Никаких предпосылок ни для какого другого linkage в этих примерах нет.
NB>ну и попутно вопрос, корректно ли в принципе делать такие функции не зависящими от включающего их типа внешнего типа?
Хм... Вроде корректно. Мне никакие ограничения по этому поводу не известны.
Best regards,
Андрей Тарасевич
Re[3]: Инжектирование друзей класса в охватывающее пространство имен
Здравствуйте, Андрей Тарасевич, Вы писали:
NB>>а какое linkage у таких функций? АТ>External, вестимо. Никаких предпосылок ни для какого другого linkage в этих примерах нет.
да, нашел
NB>>ну и попутно вопрос, корректно ли в принципе делать такие функции не зависящими от включающего их типа внешнего типа?
АТ>Хм... Вроде корректно. Мне никакие ограничения по этому поводу не известны.
тогда пример сводится к случаю:
struct T {};
inline void foo () {
T t{};
std::cout << t;
}
насколько помню, в С++03 это вроде было ОДР, но всем было пофиг.
Re[4]: Инжектирование друзей класса в охватывающее пространс
Defines a non-member function, and makes it a friend of this class at the same time. Such non-member function is always inline
Так что никакого нарушения ODR нету. И не было бы если даже такие функции не были inline. Просто бы ругнулся линкер на одинаковые функции в разных единицах трансляции
Здравствуйте, ArtDenis, Вы писали:
AD>Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>>Пусть у нас есть такое определение шаблона класса
AD>Твой случай: https://en.cppreference.com/w/cpp/language/friend (раздел Description.2): AD>
AD>Defines a non-member function, and makes it a friend of this class at the same time. Such non-member function is always inline
AD>Так что никакого нарушения ODR нету. И не было бы если даже такие функции не были inline. Просто бы ругнулся линкер на одинаковые функции в разных единицах трансляции
Не понял. К чему это здесь? Я прекрасно знаю, что эти функции inline, о чем сразу ясно сказано в моем вопросе. Как это отвечает на поставленные вопросы?
Best regards,
Андрей Тарасевич
Re[3]: Инжектирование друзей класса в охватывающее пространство име
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Не понял. К чему это здесь? Я прекрасно знаю, что эти функции inline, о чем сразу ясно сказано в моем вопросе. Как это отвечает на поставленные вопросы?
Тогда я просто не понял где именно предполагается нарушение ODR
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, Андрей Тарасевич, Вы писали:
NB>>>а какое linkage у таких функций? АТ>>External, вестимо. Никаких предпосылок ни для какого другого linkage в этих примерах нет.
NB>да, нашел
NB>>>ну и попутно вопрос, корректно ли в принципе делать такие функции не зависящими от включающего их типа внешнего типа?
АТ>>Хм... Вроде корректно. Мне никакие ограничения по этому поводу не известны.
NB>тогда пример сводится к случаю: NB>
NB>struct T {};
NB>inline void foo () {
NB> T t{};
NB> std::cout << t;
NB>}
NB>
NB>насколько помню, в С++03 это вроде было ОДР, но всем было пофиг.
Как же это он сводится?
Суть моего вопроса фактически заключается в следующем: считается ли инжектированное определение `foo` неявно зависящим от шаблонного параметра `T`. Зависящим настолько существенно, что определения `foo` в разных единицах трансляции, инжектированные разными специализациями шаблона `S` (т.е. для разных значений `T`), нарушают требования ODR.
Фактически мне просто лень было рыться в стандарте и надеялся, что кто-то мне сразу подкинет ссылку. Сейчас, прочитав http://eel.is/c++draft/basic.def.odr#13 внимательно, я прихожу к выводу, что мои предположения верны. Множественные определения `foo` соответствует требованиям, налагаемым ODR на inline-функции, а множественные определения `bar` — не соответствуют. Диагностика для `bar` не требуется, согласно http://eel.is/c++draft/basic.def.odr#15
По-прежнему может быть кто-то найдет релевантные уточняющие ссылки в разделах о шаблонах.
Здравствуйте, ArtDenis, Вы писали:
АТ>>Не понял. К чему это здесь? Я прекрасно знаю, что эти функции inline, о чем сразу ясно сказано в моем вопросе. Как это отвечает на поставленные вопросы?
AD>Тогда я просто не понял где именно предполагается нарушение ODR
Требования ODR, налагаемые на inline-функции с external linkage, фактически сводятся по своей сути к тому, что inline-функции с external linkage должны быть определены одинаково во всех единицах трансляции.
Мой вопрос сводится к тому, что такое "одинаково" для функций `foo` и `bar` из моего примера.
Best regards,
Андрей Тарасевич
Re[5]: Инжектирование друзей класса в охватывающее пространство имен
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Фактически мне просто лень было рыться в стандарте и надеялся, что кто-то мне сразу подкинет ссылку. Сейчас, прочитав http://eel.is/c++draft/basic.def.odr#13 внимательно, я прихожу к выводу, что мои предположения верны. Множественные определения `foo` соответствует требованиям, налагаемым ODR на inline-функции, а множественные определения `bar` — не соответствуют. Диагностика для `bar` не требуется, согласно http://eel.is/c++draft/basic.def.odr#15
да, я в следующем сообщении написал.
ПС: рад снова тебя здесь видеть
Re[5]: Инжектирование друзей класса в охватывающее пространство име
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Требования ODR, налагаемые на inline-функции с external linkage, фактически сводятся по своей сути к тому, что inline-функции с external linkage должны быть определены одинаково во всех единицах трансляции.
Да, уже подзабыл момент, что inline-функции должны быть полностью одинаковы в разных единицах трансляции. Похоже что нарушение ODR в данном случае всё-таки имеется
Здравствуйте, Андрей Тарасевич, Вы писали:
NB>>ну и попутно вопрос, корректно ли в принципе делать такие функции не зависящими от включающего их внешнего типа?
АТ>Хм... Вроде корректно. Мне никакие ограничения по этому поводу не известны.
кажется вспомнил почему вопрос возник.
несмотря на то что функция находитcя во внешнем пространстве, доступно она только в лексическом пространстве класса, и снаружи ее вызвать можно только по АДЛ.
Re[4]: Инжектирование друзей класса в охватывающее пространство имен
NB>>>ну и попутно вопрос, корректно ли в принципе делать такие функции не зависящими от включающего их внешнего типа?
АТ>>Хм... Вроде корректно. Мне никакие ограничения по этому поводу не известны.
NB>кажется вспомнил почему вопрос возник. NB>несмотря на то что функция находитcя во внешнем пространстве, доступно она только в лексическом пространстве класса, и снаружи ее вызвать можно только по АДЛ.
В каком смысле доступна в лексическом пространстве класса?
Re[5]: Инжектирование друзей класса в охватывающее пространство имен
Здравствуйте, σ, Вы писали:
NB>>кажется вспомнил почему вопрос возник. NB>>несмотря на то что функция находитcя во внешнем пространстве, доступно она только в лексическом пространстве класса, и снаружи ее вызвать можно только по АДЛ.
σ>В каком смысле доступна в лексическом пространстве класса?
ну, когда прочитал 11.9.4 7)
A friend function defined in a class is in the (lexical) scope of the class in which it is defined.
думал что внутри класса она все-таки видна без АДЛ...
Re[4]: Инжектирование друзей класса в охватывающее пространство имен
Здравствуйте, night beast, Вы писали:
NB>кажется вспомнил почему вопрос возник. NB>несмотря на то что функция находитcя во внешнем пространстве, доступно она только в лексическом пространстве класса, и снаружи ее вызвать можно только по АДЛ.
Ничто не мешает добавить декларацию void bar(); но от нарушения ODR это не спасёт:
#include <iostream>
void bar();
template <typename T> struct S
{
friend void bar()
{
T t{};
std::cout << t << std::endl;
}
};
int main()
{
S<int> a;
bar();
return 0;
}