Инжектирование друзей класса в охватывающее пространство име
От: Андрей Тарасевич Беларусь  
Дата: 10.01.22 19:55
Оценка: 9 (2) +1
Пусть у нас есть такое определение шаблона класса

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".

Правильны ли мои предположения?
Best regards,
Андрей Тарасевич
Отредактировано 10.01.2022 20:16 Андрей Тарасевич . Предыдущая версия .
Re: Инжектирование друзей класса в охватывающее пространство имен
От: night beast СССР  
Дата: 10.01.22 20:07
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Но что произойдет, если я сделаю два разных инстанцирования этого шаблона в разных единицах трансляции?


а какое linkage у таких функций?

ну и попутно вопрос, корректно ли в принципе делать такие функции не зависящими от включающего их типа внешнего типа?
Re[2]: Инжектирование друзей класса в охватывающее пространство имен
От: Андрей Тарасевич Беларусь  
Дата: 10.01.22 20:16
Оценка: 1 (1)
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, Андрей Тарасевич, Вы писали:


АТ>>Но что произойдет, если я сделаю два разных инстанцирования этого шаблона в разных единицах трансляции?


NB>а какое linkage у таких функций?


External, вестимо. Никаких предпосылок ни для какого другого linkage в этих примерах нет.

NB>ну и попутно вопрос, корректно ли в принципе делать такие функции не зависящими от включающего их типа внешнего типа?


Хм... Вроде корректно. Мне никакие ограничения по этому поводу не известны.
Best regards,
Андрей Тарасевич
Re[3]: Инжектирование друзей класса в охватывающее пространство имен
От: night beast СССР  
Дата: 10.01.22 20:27
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

NB>>а какое linkage у таких функций?

АТ>External, вестимо. Никаких предпосылок ни для какого другого linkage в этих примерах нет.

да, нашел

NB>>ну и попутно вопрос, корректно ли в принципе делать такие функции не зависящими от включающего их типа внешнего типа?


АТ>Хм... Вроде корректно. Мне никакие ограничения по этому поводу не известны.


тогда пример сводится к случаю:
struct T {};
inline void foo () {
   T t{};
   std::cout << t;
}


насколько помню, в С++03 это вроде было ОДР, но всем было пофиг.
Re[4]: Инжектирование друзей класса в охватывающее пространс
От: night beast СССР  
Дата: 10.01.22 20:39
Оценка:
Здравствуйте, night beast, Вы писали:

NB>насколько помню, в С++03 это вроде было ОДР, но всем было пофиг.


фигню написал
но тогда в твоем случае для bar нарушается:

name lookup from within each definition finds the same entities (after overload-resolution)

Отредактировано 10.01.2022 20:40 night beast . Предыдущая версия .
Re: Инжектирование друзей класса в охватывающее пространство име
От: ArtDenis Россия  
Дата: 10.01.22 20:40
Оценка: :)
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Пусть у нас есть такое определение шаблона класса


Твой случай: https://en.cppreference.com/w/cpp/language/friend (раздел Description.2):

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. Просто бы ругнулся линкер на одинаковые функции в разных единицах трансляции
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[2]: Инжектирование друзей класса в охватывающее пространство име
От: Андрей Тарасевич Беларусь  
Дата: 10.01.22 20:55
Оценка:
Здравствуйте, 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]: Инжектирование друзей класса в охватывающее пространство име
От: ArtDenis Россия  
Дата: 10.01.22 20:57
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Не понял. К чему это здесь? Я прекрасно знаю, что эти функции inline, о чем сразу ясно сказано в моем вопросе. Как это отвечает на поставленные вопросы?


Тогда я просто не понял где именно предполагается нарушение ODR
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[4]: Инжектирование друзей класса в охватывающее пространс
От: Андрей Тарасевич Беларусь  
Дата: 10.01.22 21:09
Оценка:
Здравствуйте, 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

По-прежнему может быть кто-то найдет релевантные уточняющие ссылки в разделах о шаблонах.
Best regards,
Андрей Тарасевич
Отредактировано 10.01.2022 21:12 Андрей Тарасевич . Предыдущая версия .
Re[4]: Инжектирование друзей класса в охватывающее пространство име
От: Андрей Тарасевич Беларусь  
Дата: 10.01.22 21:11
Оценка:
Здравствуйте, ArtDenis, Вы писали:

АТ>>Не понял. К чему это здесь? Я прекрасно знаю, что эти функции inline, о чем сразу ясно сказано в моем вопросе. Как это отвечает на поставленные вопросы?


AD>Тогда я просто не понял где именно предполагается нарушение ODR


Требования ODR, налагаемые на inline-функции с external linkage, фактически сводятся по своей сути к тому, что inline-функции с external linkage должны быть определены одинаково во всех единицах трансляции.

Мой вопрос сводится к тому, что такое "одинаково" для функций `foo` и `bar` из моего примера.
Best regards,
Андрей Тарасевич
Re[5]: Инжектирование друзей класса в охватывающее пространство имен
От: night beast СССР  
Дата: 10.01.22 21:13
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Фактически мне просто лень было рыться в стандарте и надеялся, что кто-то мне сразу подкинет ссылку. Сейчас, прочитав 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]: Инжектирование друзей класса в охватывающее пространство име
От: ArtDenis Россия  
Дата: 10.01.22 21:34
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

АТ>Требования ODR, налагаемые на inline-функции с external linkage, фактически сводятся по своей сути к тому, что inline-функции с external linkage должны быть определены одинаково во всех единицах трансляции.


Да, уже подзабыл момент, что inline-функции должны быть полностью одинаковы в разных единицах трансляции. Похоже что нарушение ODR в данном случае всё-таки имеется
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[3]: Инжектирование друзей класса в охватывающее пространство имен
От: night beast СССР  
Дата: 11.01.22 06:11
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

NB>>ну и попутно вопрос, корректно ли в принципе делать такие функции не зависящими от включающего их внешнего типа?


АТ>Хм... Вроде корректно. Мне никакие ограничения по этому поводу не известны.


кажется вспомнил почему вопрос возник.
несмотря на то что функция находитcя во внешнем пространстве, доступно она только в лексическом пространстве класса, и снаружи ее вызвать можно только по АДЛ.
Re[4]: Инжектирование друзей класса в охватывающее пространство имен
От: σ  
Дата: 11.01.22 10:02
Оценка:
NB>>>ну и попутно вопрос, корректно ли в принципе делать такие функции не зависящими от включающего их внешнего типа?

АТ>>Хм... Вроде корректно. Мне никакие ограничения по этому поводу не известны.


NB>кажется вспомнил почему вопрос возник.

NB>несмотря на то что функция находитcя во внешнем пространстве, доступно она только в лексическом пространстве класса, и снаружи ее вызвать можно только по АДЛ.

В каком смысле доступна в лексическом пространстве класса?
Re[5]: Инжектирование друзей класса в охватывающее пространство имен
От: night beast СССР  
Дата: 11.01.22 10:16
Оценка:
Здравствуйте, σ, Вы писали:

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]: Инжектирование друзей класса в охватывающее пространство имен
От: B0FEE664  
Дата: 11.01.22 13:58
Оценка:
Здравствуйте, 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;
}
И каждый день — без права на ошибку...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.