Вопрос знатокам
От: kov_serg Россия  
Дата: 03.04.23 16:50
Оценка:
// header.h
template<class T> void f(T);

//template<> void f(int);

inline void fi(int x) { f(x); }

#include "header.h"

template<> void f(int x) {}


Почему без явного объявления f(int) имеем грабли?
Зачем так сделали?
Re: Вопрос знатокам
От: T4r4sB Россия  
Дата: 03.04.23 17:04
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Почему без явного объявления f(int) имеем грабли?

_>Зачем так сделали?

Какие грабли-то?

Только что на рекстестере проверил, всё ок. (хз как там расшарить результат)
Re: Вопрос знатокам
От: rg45 СССР  
Дата: 03.04.23 17:28
Оценка: 3 (1) +3
Здравствуйте, kov_serg, Вы писали:

_>
_>// header.h
_>template<class T> void f(T);

_>//template<> void f(int);

_>inline void fi(int x) { f(x); }
_>

_>
_>#include "header.h"

_>template<> void f(int x) {}
_>


_>Почему без явного объявления f(int) имеем грабли?


Таковы требования стандарта, явная специализация должна быть видна в точке использования:

https://timsong-cpp.github.io/cppwp/temp.expl.spec#7

If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined.


_>Зачем так сделали?


Ну, наверное для того, чтоб компилятор не пытался выполнить инстанцирование праймари шаблона.

В документации по msvc есть вот такое описание ошибки: https://learn.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2908?f1url=%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(C2908)%3Bk(vs.output)%26rd%3Dtrue&amp;view=msvc-170

P.S. То, что в этом примере используется шаблон класса, а не шаблон функции, ничего по сути не меняет — требование общее.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 03.04.2023 17:43 rg45 . Предыдущая версия .
Re[2]: Вопрос знатокам
От: kov_serg Россия  
Дата: 03.04.23 20:00
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну, наверное для того, чтоб компилятор не пытался выполнить инстанцирование праймари шаблона.

// header.h
template<class T> void f(T);

//template<> void f(int);

inline void fi(int x) { f(x); }

#include "header.h"

// template<> void f(int x) {}

Зато в таком случае компилируется, но не может слинковать — нет реализации:
Отредактировано 03.04.2023 20:01 kov_serg . Предыдущая версия .
Re[3]: Вопрос знатокам
От: rg45 СССР  
Дата: 03.04.23 20:34
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>
_>// header.h
_>template<class T> void f(T);

_>//template<> void f(int);

_>inline void fi(int x) { f(x); }
_>

_>
_>#include "header.h"

_>// template<> void f(int x) {}
_>


_>Зато в таком случае компилируется, но не может слинковать — нет реализации:


И что тебе кажется неправильным во втором случае? С точки зрения компилятора, каждая единица трансляции и программа в целом well-formed. Просто отсутствует объектный файл, содержащий используемое воплощение праймари шаблона.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[3]: Вопрос знатокам
От: rg45 СССР  
Дата: 03.04.23 20:43
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>
_>// header.h
_>template<class T> void f(T);

_>//template<> void f(int);

_>inline void fi(int x) { f(x); }
_>

_>
_>#include "header.h"

_>// template<> void f(int x) {}
_>

_>Зато в таком случае компилируется, но не может слинковать — нет реализации:


Я больше скажу: если ты вынесешь специализацию в отдельный cpp-файл и не будешь подключать в этот файл никаких больше заголовков, вот так:

template<class T> void f(T); // повторное объявление вместо подключения заголовка

template<> void f(int x) {}


этим самым ты лишишь компилятор возможности продиагностировать нарушение требований и программа может даже успешно скомпилироваться. Вполне возможно, что и линкер успешно отработает и программу можно будет запустить на выполнение (я проверил на msvc — именно так все и работает). Но все равно программа при этом будет ill-formed.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 03.04.2023 20:49 rg45 . Предыдущая версия . Еще …
Отредактировано 03.04.2023 20:44 rg45 . Предыдущая версия .
Re[4]: Вопрос знатокам
От: kov_serg Россия  
Дата: 03.04.23 20:49
Оценка: +1 :)
Здравствуйте, rg45, Вы писали:

R>И что тебе кажется неправильным во втором случае? С точки зрения компилятора, каждая единица трансляции и программа в целом well-formed. Просто отсутствует объектный файл, содержащий используемое воплощение праймари шаблона.

Если добавить отдельный файл. То собирается.
// another_translation_unit.cpp
template<class T> void f(T);
template<> void f(int x) {}

Мне кажется такое поведение слегка %6@нутым неправильным.
Re[5]: Вопрос знатокам
От: rg45 СССР  
Дата: 03.04.23 20:50
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Мне кажется такое поведение слегка %6@нутым неправильным.


Есть предложения?

P.S. Ты попробуй посмотреть на ситуацию с точки зрения разработчика компилятора: у тебя есть объявление шаблона и его использование. Тебе нужно принять решение нужно ли в точке использования делать инстанцирование основного шаблона или вызвать какую-то из существующих явных специализаций. Как ты будешь принимать это решение?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 03.04.2023 20:53 rg45 . Предыдущая версия .
Re[4]: Вопрос знатокам
От: kov_serg Россия  
Дата: 03.04.23 20:52
Оценка:
Здравствуйте, rg45, Вы писали:

R>Я больше скажу: если ты вынесешь специализацию в отдельный cpp-файл и не будешь подключать в этот файл никаких больше заголовков, вот так:

R>этим самым ты лишишь компилятор возможности продиагностировать нарушение требований и программа может даже успешно скомпилироваться. Вполне возможно, что и линкер успешно отработает и программу можно будет запустить на выполнение (я проверил на msvc — именно так все и работает). Но все равно программа при этом будет ill-formed.
Я сделал примерно так:
template<class T>void f(T);

template<> void f(int); inline void fi(int x) { f(x); }

template<> void f(int x) {}

Просто выглядит вырвиглазно.
Re[6]: Вопрос знатокам
От: kov_serg Россия  
Дата: 03.04.23 20:59
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>Есть предложения?

Напиться.

R>P.S. Ты попробуй посмотреть на ситуацию с точки зрения разработчика компилятора: у тебя есть объявление шаблона и его использование. Тебе нужно принять решение нужно ли в точке использования делать инстанцирование основного шаблона или вызвать какую-то из существующих явных специализаций. Как ты будешь принимать это решение?


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

ps:
void ok() { void fn(); fn(); }
void fail(int x) { template<> void f(int); f(x); } // с чем связан запрет подобных объявлений?
Отредактировано 03.04.2023 21:05 kov_serg . Предыдущая версия .
Re[5]: Вопрос знатокам
От: rg45 СССР  
Дата: 03.04.23 21:01
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Я сделал примерно так:

_>
_>template<class T>void f(T);

_>template<> void f(int); inline void fi(int x) { f(x); }

_>template<> void f(int x) {}
_>

_>Просто выглядит вырвиглазно.

Ну тут мне трудно что-либо сказать, не зная задачи, которую ты решаешь и не видя картины в целом. Лишь пара общих соображений. Во-первых, ИМХО(!) код выглядел бы более удобочитаемым, если бы не твое стремление записысывать отдельные языковые конструкции в одну строчку. Во-вторых, ориентируясь на свой опыт, мне трудно вспомнить случай, когда специализация шаблона функции была бы целесообразна (при условии, что весь код твой). Я считаю, что практически всегда вместо специализации можно использовать перегрузку и это будет лучше. Лично у меня любое использование специализаций шаблонов функций вызывает подозрение на не очень удачный дизайн.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[6]: Вопрос знатокам
От: kov_serg Россия  
Дата: 03.04.23 21:16
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>Ну тут мне трудно что-либо сказать, не зная задачи, которую ты решаешь и не видя картины в целом. Лишь пара общих соображений. Во-первых, ИМХО(!) код выглядел бы более удобочитаемым, если бы не твое стремление записысывать отдельные языковые конструкции в одну строчку. Во-вторых, ориентируясь на свой опыт, мне трудно вспомнить случай, когда специализация шаблона функции была бы целесообразна (при условии, что весь код твой). Я считаю, что практически всегда вместо специализации можно использовать перегрузку и это будет лучше. Лично у меня любое использование специализаций шаблонов функций вызывает подозрение на не очень удачный дизайн.

Просто каждая строка отдельное действие. Но это не суть.

Всплыло это случайно при сериализации.
struct Helper {
  Tape &tape; bool store;
  template<class T> void process(T &t);
  template<class T> Helper& operator<< (T &t) { process(t); return *this; }
  Helper(Tape &tape,bool store,int id) : tape(tape), store(store) { process(id); }
};
Re[7]: Вопрос знатокам
От: rg45 СССР  
Дата: 03.04.23 21:26
Оценка: +1
Здравствуйте, kov_serg, Вы писали:

_>С точки зрения компилятора, я не инстанцирую класс, а вызываю функцию. Что явно указано в коде. Нахрена запрещать определение реализации дальше по коду. Почему это не эквивалентно костылю с предварительным насильным объявлением сигнатуры функции.


Мне кажется, что сейчас твое внимание сконцентрировано на конкретном случае и это мешает тебе рассмотреть другие возможные варианты. Допустим, все реализовано как ты хочешь: в точке использования компилятор видит только лишь объявление основного шаблона и шьет такой код, который позволяет линкеру подшить нужную специализацию в точку вызова. Но что если после этого программист решает переместить определение основного шаблона в заголовок? Тогда, после обработки сопроцессором всех директив компилятор получит такой код:

template<class T>
void f(T t)
{
  // Do smth with t
}

void fi(int x)
{
  f(x);
}


Поведение программы должно измениться или нет? Наверное, нет, иначе это будет действительно странное поведение. Но как тогда в точке вызова компилятор должен понять, что здесь ему не нужно инстанцировать основной шаблон? Как быть с неявным инстанцированием, которое обеспечивает именно компилятор — отменяем?

Второй момент: что если это не int, а какой-то сложный тип (например, использующий наследование) под который подходят сразу несколько специализаций, которые к тому же и не видны в данной точке? Что у нас с будет с overload resolution в твоем подходе? Не перекладывать же эту работу на линкер?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 03.04.2023 21:40 rg45 . Предыдущая версия . Еще …
Отредактировано 03.04.2023 21:29 rg45 . Предыдущая версия .
Отредактировано 03.04.2023 21:27 rg45 . Предыдущая версия .
Отредактировано 03.04.2023 21:27 rg45 . Предыдущая версия .
Re[8]: Вопрос знатокам
От: kov_serg Россия  
Дата: 03.04.23 21:40
Оценка:
Здравствуйте, rg45, Вы писали:


R>Мне кажется, что сейчас твое внимание сконцентрировано на конкретном случае и это мешает тебе рассмотреть другие возможные варианты. Допустим, все реализовано как ты хочешь: в точке использования компилятор видит только лишь объявление основного шаблона и шьет такой код, который позволяет линкеру подшить нужную специализацию в точку вызова.


R>Но что если после этого программист решает переместить определение основного шаблона в заголовок? Тогда, после обработки сопроцессором всех директив компилятор получит такой код:


R>
R>template<class T>
R>void f(T t)
R>{
R>  // Do smth with t
R>}

R>void fi(int x)
R>{
R>  f(x);
R>}
R>

Вот когда он это сделает то ситуация уже другая. Тут есть что инстанцировать. До этого было только объявление без дефолтной реализации, вообще без какой либо реализации совсем.

R>Как в точке вызова компилятор должен понять, что здесь ему не нужно инстанцировать основной шаблон? Как быть с неявным инстанцированием, которое обеспечивает именно компилятор — отменяем?

В предыдущем варианте небыло ни строчки про то что надо что-либо инстанцировать, только объявление.

R>Второй момент: что если это не int, а какой-то сложный тип (например, использующий наследование) под который подходят сразу несколько специализаций, которые к тому же и не видны в данной точке? Что у нас с будет с overload resolution в твоем подходе? Не перекладывать же эту работу на линкер?

В чем собственно разница? Всё должно работать единообразно, но это не про стандарт c++
Re[9]: Вопрос знатокам
От: rg45 СССР  
Дата: 03.04.23 21:43
Оценка:
Здравствуйте, kov_serg, Вы писали:

R>>
R>>template<class T>
R>>void f(T t)
R>>{
R>>  // Do smth with t
R>>}

R>>void fi(int x)
R>>{
R>>  f(x);
R>>}
R>>

_>Вот когда он это сделает то ситуация уже другая. Тут есть что инстанцировать. До этого было только объявление без дефолтной реализации, вообще без какой либо реализации совсем.

Ну вот это неочевидный вывод, для меня, по крайней мере. Если при перемещении определения шаблона функции из срр-файла в заголовок будет меняться видимое поведение программы, то это уже действительно странно.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[9]: Вопрос знатокам
От: rg45 СССР  
Дата: 03.04.23 21:46
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>В предыдущем варианте небыло ни строчки про то что надо что-либо инстанцировать, только объявление.


И тем не менее, это определение может существовать где-то в соседней единице трансляции. Если его нет сейчас, то его можно добавить в любой момент.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[10]: Вопрос знатокам
От: kov_serg Россия  
Дата: 03.04.23 21:53
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну вот это неочевидный вывод, для меня, по крайней мере. Если при перемещении определения шаблона функции из срр-файла в заголовок будет меняться видимое поведение программы, то это уже действительно странно.

Чего тут странного. Поведение не должно меняться.
template<class T> void f0(T&);
template<class T> void f1(T&) {}

inline void f0i(int x) { f0(x); }
inline void f1i(int x) { f1(x); }
//...
// template<> void f1(int&) {} // ошибка, уже есть реализация, полученная при инстанцировании
// template<> void f0(int&) {} // ошибка, просто за компанию. реализаци не ну и пофиг всё равно нельзя.

Надо явно компилятору дополнительно пальцем тыкать. Как буд-то он не в курсе что у него нет никакой дефолтной имлементации.
template<class T> void f0(T&);
template<class T> void f1(T&) {}

template<> void f0(int &); inline void f0i(int x) { f0(x); }
inline void f1i(int x) { f1(x); }
//...
// template<> void f1(int&) {} // ошибка, уже есть реализация, полученная при инстанцировании
template<> void f0(int&) {} // теперь можно

Разве не видно неконсистентность?
Re[10]: Вопрос знатокам
От: kov_serg Россия  
Дата: 03.04.23 21:57
Оценка:
Здравствуйте, rg45, Вы писали:

R>И тем не менее, это определение может существовать где-то в соседней единице трансляции. Если его нет сейчас, то его можно добавить в любой момент.

template<class T>void f(T t);
template<class T>void g(T t);

inline void fi(int x) { f(x); }

template<class T>void f(T t) { g(t);}
template<> void g(int x) {}

Имеется ввиду так?
Re[11]: Вопрос знатокам
От: rg45 СССР  
Дата: 03.04.23 22:19
Оценка:
Здравствуйте, kov_serg, Вы писали:

R>>И тем не менее, это определение может существовать где-то в соседней единице трансляции. Если его нет сейчас, то его можно добавить в любой момент.

_>
_>template<class T>void f(T t);
_>template<class T>void g(T t);

_>inline void fi(int x) { f(x); }

_>template<class T>void f(T t) { g(t);}
_>template<> void g(int x) {}
_>

_>Имеется ввиду так?

Примерно так:

// header.h

template<class T> void f(T t);

inline void fi(int x) { f(x); }


// main.cpp
#include header.h

int main()
{
   fi(42);
}


// separate.cpp

// Primary template definition
template<typename T>
void f(T t)
{
   // . . .
}

// Explicit template instantiations
template void f<char>(char);
template void f<short>(short);
template void f<double>(double);

// Explicit specialization for f<int>
template<>
void f<int>(int i)
{
   // . . .
}


Если собрать этот проект под msvc, то будет работать в точности как ты хочешь. Но потом кто-то решает, что нужно перенести определение основного шаблона функции f из separate.cpp в header.h. При этом специализация f<int> остается в separate.cpp и в точке использования в fi по-прежнему не видна. Должно измениться поведение программы после такого перенесения или нет?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 03.04.2023 22:25 rg45 . Предыдущая версия .
Re[12]: Вопрос знатокам
От: kov_serg Россия  
Дата: 03.04.23 22:34
Оценка:
Здравствуйте, rg45, Вы писали:


R>Если собрать этот проект под msvc, то будет работать в точности как ты хочешь. Но потом кто-то решает, что нужно перенести определение основного шаблона функции f из separate.cpp в header.h. При этом специализация f<int> остается в separate.cpp и в точке использования в fi по-прежнему не видна. Должно измениться поведение программы после такого перенесения или нет?

Так много вариантов есть всё переломать. Это один из них. Тут ситуация я так понял проще.
template<class T>void f(T); // заявляем что есть скоро будетет такой дефолтный метод

inline void fi(int x) { f(x); } // используем дефолтную реализацию f<int> пофиг что её пока нет, вруг ниже есть

template<class T>void f(T) {} // и тут фигакс вот он. теперь ок. но если мы его не объявим ... то ub (ну вы вкурсе програмист сам дурак по стандарту во всех спорных ситуациях)

Проблемма в том что дефолтный f(T) может быть определён позже и отсюда вся эта лажа и вытекает.
Re: Вопрос знатокам
От: B0FEE664  
Дата: 04.04.23 10:30
Оценка:
Здравствуйте, kov_serg, Вы писали:

  Скрытый текст
_>
_>// header.h
_>template<class T> void f(T);

_>//template<> void f(int);

_>inline void fi(int x) { f(x); }
_>

_>
_>#include "header.h"

_>template<> void f(int x) {}
_>

_>Почему без явного объявления f(int) имеем грабли?
_>Зачем так сделали?


// header.h
template<class T> void f(T);

//template<> void f(int);

inline void fi(int x) { f(x); }


/// @file impl_int.cpp
#include "header.h"

template<> void f(int x) {}


/// @file impl_float.cpp
#include "header.h"

template<> void f(float x) {}

void test()
{
  fi(1);
}


Которая из функций f() должна быть вызвана из функции test() ?
И каждый день — без права на ошибку...
Re[2]: Вопрос знатокам
От: night beast СССР  
Дата: 04.04.23 10:37
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>
BFE>/// @file impl_float.cpp
BFE>#include "header.h"

BFE>template<> void f(float x) {}

BFE>void test()
BFE>{
BFE>  fi(1);
BFE>}
BFE>


BFE>Которая из функций f() должна быть вызвана из функции test() ?


а как наличие или отсутствие левой специализации должно повлиять на выбор?
у тебя там не перегрузка.
Re[3]: Вопрос знатокам
От: B0FEE664  
Дата: 04.04.23 14:46
Оценка:
Здравствуйте, night beast, Вы писали:

BFE>>Которая из функций f() должна быть вызвана из функции test() ?

NB>а как наличие или отсутствие левой специализации должно повлиять на выбор?
Вот пусть kov_serg и ответит, что в таком случае должно быть.
Я рассчитываю на то, что в попытке найти ответ на этот вопрос, kov_serg поймёт зачем нужна декларация специализации перед использованием.

NB>у тебя там не перегрузка.

Компилируя файл impl_float.cpp компилятор вообще ничего не знает о template<> void f(int x) {} из файла impl_int.cpp. Вот мне и интересно, что именно, с точки зрения kov_serg, должно происходить.
И каждый день — без права на ошибку...
Re[4]: Вопрос знатокам
От: kov_serg Россия  
Дата: 04.04.23 17:57
Оценка:
Здравствуйте, B0FEE664, Вы писали:

NB>>а как наличие или отсутствие левой специализации должно повлиять на выбор?

BFE>Вот пусть kov_serg и ответит, что в таком случае должно быть.
BFE>Я рассчитываю на то, что в попытке найти ответ на этот вопрос, kov_serg поймёт зачем нужна декларация специализации перед использованием.

NB>>у тебя там не перегрузка.

BFE>Компилируя файл impl_float.cpp компилятор вообще ничего не знает о template<> void f(int x) {} из файла impl_int.cpp. Вот мне и интересно, что именно, с точки зрения kov_serg, должно происходить.
Отвечаю:
void f(int&);
void f(double&);
void f(short&);

Вот чтоб так не писать такую портянку. Хочется сказать компилятору что вот такого вида функции можно использовать но они объявлены где-то в других файлах.
template<class T>void f(T&);

Но компилятор на это смотрит через жопу иначе. Он считает что эта декларация означает что программист объявил реализацию по умолчанию, но потом.
И вот если этого потом не появилось, то возникают грабли.
Re[5]: Вопрос знатокам
От: B0FEE664  
Дата: 04.04.23 21:18
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>
_>void f(int&);
_>void f(double&);
_>void f(short&);
_>

_>Вот чтоб так не писать такую портянку. Хочется сказать компилятору что вот такого вида функции можно использовать но они объявлены где-то в других файлах.
_>[cpp]

Ой, тоже мне проблема!

/// @file header.h
template<class T, class FnF>
inline void fi(T& x, FnF&& f) { f(x); }


/// @file main.cpp
#include <iostream>

void f_int(int&)
{
  std::cout << "int\n";
}

int main()
{
  int i = 0;
  fi(i, f_int);

  float x = 1.f;
  fi(x, [](float& rX) { std::cout << "float " << rX << '\n';} );

  std::cout << "the end\n";
  return 0;
}

И каждый день — без права на ошибку...
Re[6]: Вопрос знатокам
От: kov_serg Россия  
Дата: 04.04.23 21:56
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Ой, тоже мне проблема!

BFE>
BFE>/// @file header.h
BFE>template<class T, class FnF>
BFE>inline void fi(T& x, FnF&& f) { f(x); }
BFE>

BFE>
Поздравляю вы решили совершенно другую задачу.
Re[5]: Вопрос знатокам
От: rg45 СССР  
Дата: 04.04.23 21:57
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Отвечаю:

_>
_>void f(int&);
_>void f(double&);
_>void f(short&);
_>

_>Вот чтоб так не писать такую портянку. Хочется сказать компилятору что вот такого вида функции можно использовать но они объявлены где-то в других файлах.
_>
_>template<class T>void f(T&);
_>

_>Но компилятор на это смотрит через жопу иначе. Он считает что эта декларация означает что программист объявил реализацию по умолчанию, но потом.
_>И вот если этого потом не появилось, то возникают грабли.

Чтоб портянки не писать проектировать нужно нормально, по максимуму используя ADL. По-моему, только ленивый еще не писал "не специализируйте шаблоны функций".
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[6]: Вопрос знатокам
От: kov_serg Россия  
Дата: 04.04.23 22:05
Оценка:
Здравствуйте, rg45, Вы писали:

R>Чтоб портянки не писать проектировать нужно нормально, по максимуму используя ADL. По-моему, только ленивый еще не писал "не специализируйте шаблоны функций".

Без примера не понятно.
Re[7]: Вопрос знатокам
От: rg45 СССР  
Дата: 04.04.23 22:11
Оценка:
Здравствуйте, kov_serg, Вы писали:

R>>Чтоб портянки не писать проектировать нужно нормально, по максимуму используя ADL. По-моему, только ленивый еще не писал "не специализируйте шаблоны функций".


_>Без примера не понятно.


http://coliru.stacked-crooked.com/a/4b8d981c3e71c47b

#include <iostream>
#include <string>

template <typename T>
void fi(T&& t)
{
    f(t); // вообще никакого предварительного объявления не требуется
}

namespace test
{
// А типы вместе с их функциями могут располагаться в разных пространствах имен
struct A
{
    std::string name;
};

void f(const A& a)
{
   std::cout << "A: " << a.name << std::endl;
}

} // namespace test

int main()
{
    fi(test::A{"RSDN"});
}


Сразу оговорюсь, что для встроенных типов ADL таким образом не работает. Но я сильно сомневаюсь, что упомянутые выше "портянки" появляются исключительно от использования встроенных типов.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 04.04.2023 22:35 rg45 . Предыдущая версия .
Re[8]: Вопрос знатокам
От: kov_serg Россия  
Дата: 04.04.23 22:48
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, kov_serg, Вы писали:


R>http://coliru.stacked-crooked.com/a/4b8d981c3e71c47b

Тут можно знатно огрести и потом долго отлаживать.

R>Сразу оговорюсь, что для встроенных типов ADL таким образом не работает. Но я сильно сомневаюсь, что упомянутые выше "портянки" появляются исключительно от использования встроенных типов.

Нет для базовых типов только не для всех, а для этих bool,char,unsigned char,signed char,int,unsigned int,short,unsigned short,long,unsigned long,long long,unsigned long long,float,double
для более специализированных типа std::string и хочется что бы оно искало функцию по сигнатуре. если такая объявлена то пусть использует если нет, то не собирается.

Вот что я хотел:
// header
struct A {
    template<class T>void f(T& t) { void ext_f(A*,T&); ext_f(this,t); }
};

// impl
#include <stdio.h>
#include <string>
using std::string;
void ext_f(A* self,char &v) { printf("char=%d\n",v); }
void ext_f(A* self,signed char &v) { printf("signed char=%d\n",v); }
void ext_f(A* self,unsigned char &v) { printf("unsigned char=%d\n",v); }
void ext_f(A* self,int &v)  { printf("int=%d\n",v); }
void ext_f(A* self,string &v)  { printf("string=%s\n",v.c_str()); }

int main(int argc, char const *argv[]) {
    A a[1]; char c=1; signed char cs=2; unsigned char cu=3; 
    int i=4; string s="string";
    a->f(c);
    a->f(cs);
    a->f(cu);
    a->f(i);
    a->f(s);
    return 0;
}

char=1
signed char=2
unsigned char=3
int=4
string=string
Отредактировано 04.04.2023 23:07 kov_serg . Предыдущая версия . Еще …
Отредактировано 04.04.2023 23:02 kov_serg . Предыдущая версия .
Re[9]: Вопрос знатокам
От: kov_serg Россия  
Дата: 05.04.23 11:23
Оценка:
C++ extension functions
struct A {
  template<class... T> A& f(T&...t) { void ext_f(A*,T&...); ext_f(this,t...); return *this; }
};

Всё таки C++ наркоманский язык.
Re[10]: Вопрос знатокам
От: rg45 СССР  
Дата: 05.04.23 11:55
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>C++ extension functions

_>
_>struct A {
_>  template<class... T> A& f(T&...t) { void ext_f(A*,T&...); ext_f(this,t...); return *this; }
_>};
_>

_>Всё таки C++ наркоманский язык.

А объявление ext_f нафига здесь — чтоб код труднее читать было и чтоб ADL поломать?

http://coliru.stacked-crooked.com/a/7aa93214da386f99

#include <iostream>

struct A {

    template<class... T>
    A& f(T&&...t) { 
        ext_f(*this, t...);
        return *this;
    }
};

void ext_f(A&, int i)
{
    std::cout << "ext_f(A&, " << i << ")" << std::endl;
}

template <typename...T>
void ext_f(A&, T&&...t)
{
    ((std::cout << t << " "), ...);
}

int main()
{
    A a;
    a.f(42); // -> ext_f(A&, 42);
    a.f("Hello", 3.14, 'A'); // -> Hello 3.14 A
}


P.S. Обрати внимание, я специально подобрал два таких варианта исполнения ext_f, которые не укладываются в одно общее объявление (хотя бы потому, что одна функция обычная, а другая — шаблон). Тем не менее, оба эти варианта спокойно подхватываются внутри A::f.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 05.04.2023 12:49 rg45 . Предыдущая версия . Еще …
Отредактировано 05.04.2023 12:48 rg45 . Предыдущая версия .
Отредактировано 05.04.2023 11:59 rg45 . Предыдущая версия .
Re[10]: Вопрос знатокам
От: rg45 СССР  
Дата: 05.04.23 12:30
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>C++ extension functions

_>
_>struct A {
_>  template<class... T> A& f(T&...t) { void ext_f(A*,T&...); ext_f(this,t...); return *this; }
_>};
_>

_>Всё таки C++ наркоманский язык.

Я думаю, что тебе интересно будет прочитать вот эту статью (если еще не читал):

http://programming-lang.com/ru/comp_programming/satter/0/j118.html

Все свободные функции, определенные в одном пространстве имен с каким-либо классом и предназначенные для выполнения каких-то действий с объектами этого класса, по факту, являются частью открытого интерфейса этого класса. Ну или расширениями — название особо сути не меняет. И это базовые принципы, без каких-либо ухищрений.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[11]: Вопрос знатокам
От: B0FEE664  
Дата: 05.04.23 12:49
Оценка: +1
Здравствуйте, rg45, Вы писали:

_>>C++ extension functions

_>>
_>>struct A {
_>>  template<class... T> A& f(T&...t) { void ext_f(A*,T&...); ext_f(this,t...); return *this; }
_>>};
_>>

_>>Всё таки C++ наркоманский язык.

R>А объявление ext_f нафига здесь — чтоб код труднее читать было и чтоб ADL поломать?


Разумным предположением может быть: чтобы по ошибке не сделать void ext_f(A*, int i), вместо void ext_f(A*, int& i)
но, "боюсь", к автору сего это не относится.
И каждый день — без права на ошибку...
Re[12]: Вопрос знатокам
От: rg45 СССР  
Дата: 05.04.23 12:53
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Разумным предположением может быть: чтобы по ошибке не сделать void ext_f(A*, int i), вместо void ext_f(A*, int& i)

BFE>но, "боюсь", к автору сего это не относится.

Ну то есть, это имеет смысл делать только в том случае, когда нужно ограничить число возможных вариантов подстановки. Причем, ограничить резко — в этом случае даже ADL уже не будет работать, а вместо этого компилятор будет искать одну конкретную функцию в текущем пространстве имен, в точности совпадающую по сигнатуре с объявленной. И я тоже думаю, что это вряд ли тот эффект, которого хочет добиться автор.

P.S. А, или даже хуже того — компилер ничего искать не будет, он просто сгенерирует вызов, соответствующий объявлению, и все, а искать эту функцию будет уже линкер.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 05.04.2023 13:19 rg45 . Предыдущая версия . Еще …
Отредактировано 05.04.2023 13:03 rg45 . Предыдущая версия .
Отредактировано 05.04.2023 13:02 rg45 . Предыдущая версия .
Отредактировано 05.04.2023 12:56 rg45 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.