Ошибка компилятора - неправильная перегрузка методов - страница 4

 
A100:

А почему шаблоны должны отличаться? Если в базовом классе есть лучшая функция то выбирается она... а специализация лучше шаблона по определению (тот же C++ при наличии шаблона и специализации выбирает специализацию). Если шаблон в производном будет лучше специализации в базовом то при одновременном сохранении этого правила будет полная неразбериха

В данном примере лучшая функция (специализация) находится в производном классе.  Но она не выбирается.

Если шаблон в производном будет лучше специализации в базовом то при одновременном сохранении этого правила будет полная неразбериха

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

p.s. Рассуждаю только из того, что вижу.  Если у вас есть ссылка на чёткие правила, то будет лучше

 

Alexey Navoykov:

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

С++ выбирает ту что в производном и в случае шаблона и в случае кастинга. Было бы странно если бы MQL руководствовался в случае шаблона одним принципом, а в случае кастинга противоположным
 
A100:
С++ выбирает ту что в производном и в случае шаблона и в случае кастинга. Было бы странно если бы MQL руководствовался в случае шаблона одним принципом, а в случае кастинга противоположным

Ок, ну смотрите.  Вот примерно ваш случай:

struct A { static void f(int) {} };

struct B:A { static void f(double) {} };

int a;
B::f(a);

В методе A::f() программист заложился на один тип.  В методе B::f() - на другой.  И неизвестно, какой именно тип имел ввиду пользователь, и какие последствия будут от приведения типа.

А вот в случае шаблона

template<typename T>
struct X { };

struct A { static template<typename T> void f(X<T>&) {} };

struct B:A { static template<typename T> void f(T&) {} };

X<int> x;
B::f(x);

в обоих функциях программист заложился на тип X<int> , просто в одном случае это указано более чётко, а в другом менее.  Но в обоих случаях это точно совпадает с типом принимаемого значения.  Информации о типе не теряется.  Так с какой стати нужно идти в родительский класс?    Данный случай эквивалентен, как если бы в обоих классах была перегружена функция с абсолютно одинаковой сигнатурой:  void f(X<int>&)

 
A100:

Как минимум не логично предъявлять Разработчикам ошибку без правильного кода

не хотел без компилятора что-то писать, у меня дома нет 5ки. Поэтому отложил до утра. Вот код:

template<typename T>
struct A
{
  int _a;
  void a() { };
};

struct _C
{
  template<typename T>
  static void f(T& t) { t.a(); Print("1");}
};

struct C : _C
{
  template<typename T>
  static void f(A<T>& a) { a.a(); Print("2");};
};

struct _D
{
  template<typename T>
  static void f(A<T>& a) { a.a(); Print("2");};
};

struct D : _D
{
  template<typename T>
  static void f(T& t) { t.a(); Print("1");}
};

void OnStart()
{ 
  A<int> a;
  C::f(a);
  D::f(a);
}

2018.09.13 10:39:25.244	test3 (EURUSD,M5)	1
2018.09.13 10:39:25.244	test3 (EURUSD,M5)	1

Мы видим 1 в обоих случаях. По логике должно быть или 1 2 или 2 2.

Итого получается что

1. у специализаций базового и наследника одинаковый приоритет

2. выбирается менее узкая специализация, что в любом случае неправильно.

будем надеяться что когда перегрузка шаблонных функций будет реализована, (2) починится автоматом.

 
Alexey Navoykov:

Ок, ну смотрите.  Вот примерно ваш случай:

В методе A::f() программист заложился на один тип.  В методе B::f() - на другой.  И неизвестно, какой именно тип имел ввиду пользователь, и какие последствия будут от приведения типа.

А вот в случае шаблона

в обоих функциях программист заложился на тип X<int> , просто в одном случае это указано более чётко, а в другом менее.  Но в обоих случаях это точно совпадает с типом принимаемого значения.  Информации о типе не теряется.  Так с какой стати нужно идти в родительский класс?    Данный случай эквивалентен, как если бы в обоих классах была перегружена функция с абсолютно одинаковой сигнатурой:  void f(X<int>&)

Так я и не понимаю: если f(int) может вызываться в Вашем примере вместо f(double), то почему f(X<T>&) не может вызываться вместо f(T&). И наоборот: если f(int) не может вызываться вместо f(double), то почему f(X<T>&) может вызываться вместо f(T&). Почему разработчик и пользовать класса в случае кастинга могут иметь ввиду разное, а в случае шаблонов непременно будут иметь ввиду одно и тоже. Да... тип сохраняется... но он может вообще в дальнейшем не использоваться (как и значение)... главное что тело методов разное а значит может быть разный результат.

Принцип стабильности он либо соблюдается... и тогда везде единый порядок... либо не соблюдается... и тогда неразбериха: в случае кастинга один порядок... в случае шаблонов другой... в случае константности третий... (константность ещё не затрагивали - а там в MQL другой порядок) и возникает необходимость все эти порядки учитывать

 
A100:

Так я и не понимаю: если f(int) может вызываться в Вашем примере вместо f(double), то почему f(X<T>&) не может вызываться вместо f(T&). И наоборот

Потому что f(double) - это не точная сигнатура, требующая приведения.  А шаблон f (T&) раскрывается в точную сигнатуру.  Поэтому я не вижу противоречия.  Производится поиск от производного класса к родительскому. И как только находится точная сигнатура, то используем её.

Да... тип сохраняется... но он может вообще в дальнейшем не использоваться (как и значение)... главное что тело методов разное а значит может быть разный результат.

Очевидно программист должен позаботиться об этом, коль он взял на себя ответственность принимать любой тип переменной.

Принцип стабильности он либо соблюдается... и тогда везде единый порядок... либо не соблюдается... и тогда неразбериха

Возможно и так... Но я пока не могу себе представить неразбериху, т.к. ощущаю разницу между шаблонным типом и приведением типа.

Но по большому счёту, надо действительно менять весь порядок, включая и перегрузку обычных методов, чтоб было как в С++.    Я вот сначала, прочитав их аргументы про "палку о двух концах", посчитал что там действительно нет однозначного решения.  Но подумав, понял что эти концы совершенно не равноценны, из двух зол надо выбирать наименьшее.   Создатель производного класса всё знает о методах родительского, поэтому производя перегрузку он может (и должен) учесть их.  А родитель ничего не знает о наследниках.

// Впрочем ладно, теперь уже вряд что-то станут менять в этом плане.

Причина обращения: