Вопрос по типизации

 

Уважаемые программисты, давно ломаю голову над вопросом. Можно ли как-то исхитриться и сделать неявную типизацию возвращаемого значения функции? Чтобы метод класса возвращал значение различного типа при внешней идентичности обращения к методам.

void OnStart()
 {
  CParameter p1 = Ask, p2 = Bid, p3 = TimeCurrent(), p4 = TimeLocal();
  
  // Перегруженный в CParameter оператор ~ возвращает преобразованное в строку значение известного объекту типа
  
  printf( "p1=%s, p2=%s, p3=%s, p4=%s", ~p1, ~p2, ~p3, ~p4 );
  
  // А можно ли исхитриться и сделать вот так (допустим это будет перегруженный тем или иным образом оператор !, возвращающий
  //   значение нужного типа в зависимости от type. 
  //
  // double ask = !p1, bid = !p2;
  // datetime current = !p3, local = !p4;  
 }  

Это сам класс, его можно не смотреть, если вопрос понятен.

enum TYPE{ NUMBER, PRICE, TIME };

class CParameter
 {
public:
  template<typename T> void operator= ( T par )
   {
    if( typename( T ) == "double" )
     {
      data = int( double( par ) * 100000 );
      type = PRICE;
     }
    else
     {
      if( typename( T ) == "datetime" )
       {
        data = int( uint( par ) );
        type = TIME;
       }
      else
       {
        data = int( par );
        type = NUMBER;
       }
     }
   }
   
  double to_price()
   {
    switch(type)
     {
      case PRICE: return double( data ) / 100000;
      case TIME:  return MathArcsin(2.0);
      default:    return double( data );
     }
   }

  datetime to_time()
   {
    switch(type)
     {
      case TIME:  return datetime( data );
      default:    return 0;
     }
   }

  int to_number()
   {
    switch(type)
     {
      case PRICE: return int( double( data ) / 100000 );
      case TIME:  return 0;
      default:    return data;
     }
   }
   
  string to_string()
   {
    switch(type)
     {
      case PRICE: return DoubleToString( to_price(), 5 );
      case TIME:  return TimeToString( to_time(), TIME_DATE|TIME_SECONDS );
      default:    return IntegerToString( data );
     }
   }
   
  string operator~ ()
   {
    return to_string();
   }
   
private:
  int data;
  TYPE type;
 };
 

Можно еще и при присвоении избавиться от swich и воспользоваться возможностями перегрузки:

   void operator=(double par){
      data = int(double(par)*100000 );
      type = PRICE;
   }
   void operator=(datetime par){
      data=int(uint(par));
      type=TIME;
   }
   void operator=(int par){   
      data = int(par);
      type = NUMBER;
   }

Потом, если возвращаемое значение будет использоваться для арифметического действия (что бы было на что ориентироваться), то примерно так же: 

   string operator+(string par){
      return to_string()+par;
   }
   
   double operator+(double par){
      return to_price()+par;
   }   
   
   double operator+(int par){
      return to_number()+par;
   }  
Print(p1+1);
Print(p1+1.0);
Print(p1+"1");

---

Если бы себе, сделал бы перегруженные функции с возвратом через параметр по ссылке.

   void r(double & v){
      v=to_price();
   }
   void r(int & v){
      v=to_number();
   }   
   void r(string & v){
      v=to_string();
   }   
 
Dmitry Fedoseev:

Можно еще и при присвоении избавиться от swich и воспользоваться возможностями перегрузки:

Потом, если возвращаемое значение будет использоваться для арифметического действия (что бы было на что ориентироваться), то примерно так же: 

С явной перегрузкой и ариф.действиями и так все понятно. Да, перегруженный = явно удачнее в этом случае, Вы правильно заметили, просто я на скорую руку сооружал пример для вопроса и не особенно о нем задумывался (хотя там number не только int, a ещё char, uchar, short, ushort, uint, bool и color, так что не все однозначно )))))))

Dmitry Fedoseev:

Если бы себе сделал бы перегруженные функции с возвратом через параметр по ссылке.


Тут вопрос в том, чтобы тип возвращаемого значения перегруженного метода зависел от содержания объекта.

Например, имеем разнотиповый массив (примеры приводить не буду, т.к. многобуков). И, разумеется, в любом массиве должна быть операция [] индексации, возвращающая значение переменной с соответствующим индексом.

Но переменные имеют разные типы. Там может быть datetime, а может быть string, или вообще какой-нибудь пользовательский тип под этим индексом. И хотелось бы в идеале, чтобы тип возвращаемого значения при индексации [] автоматически выдавал соответствующий тип, без необходимости его явно параметризовывать. Раньше я эту "проблему" решал так: var[(char)1], var[(short)1], var[(uint)1] и т.п. но эти костыли никуда не годятся.

 
void OnStart()
 {
  CParameter<double> p1 = Ask;
  CParameter<double> p2 = Bid;
  CParameter<datetime> p3 = TimeCurrent();
  CParameter<datetime> p4 = TimeLocal();
  
  // Перегруженный в CParameter оператор ~ возвращает преобразованное в строку значение известного объекту типа
  
  printf( "p1=%s, p2=%s, p3=%s, p4=%s", ~p1, ~p2, ~p3, ~p4 );
  
  // А можно ли исхитриться и сделать вот так (допустим это будет перегруженный тем или иным образом оператор !, возвращающий
  //   значение нужного типа в зависимости от type. 
  //
   double ask = !p1, bid = !p2;
   datetime current = !p3, local = !p4;  
 }  
template <typename T>
class CParameter
 {
public: 
  void operator =( const T Value )
  {
    this.data = Value;
  }

  string operator~( void) const
   {
    return((string)this.data);
   }
   
  T operator !( void ) const
  {
    return(this.data);
  }  
   
private:
  T data;
 };
 
fxsaber:

В этом случае не присвоишь p1 значение p4 (со сменой типа), а это принципиально должно быть возможно. к томуже объекты должны быть одного типа.

Ну или в случае массива - объекты разных типов с одним базовым, но сам объект массива имеет метод, тип возврата которого зависит от типа объекта с соответствующим индексом
 
схожий вопрос: почему при перегрузке метода (в сигнатуре перегруженного метода) не фигурирует тип возврата, а фигурируют только типы параметров. то есть нельзя определить два идентичных метода с разными типами возвращаемого значения. почему сделано такое ограничение? какой в этом смысл, почему нельзя сделать перегрузку метода по типу возвращаемого значения при идентичных параметрах
 
Ilya Malev:

В этом случае не присвоишь p1 значение p4 (со сменой типа), а это принципиально должно быть возможно. к томуже объекты должны быть одного типа.

Ну или в случае массива - объекты разных типов с одним базовым, но сам объект массива имеет метод, тип возврата которого зависит от типа объекта с соответствующим индексом

https://www.mql5.com/ru/docs/constants/structures/mqlparam

Документация по MQL5: Константы, перечисления и структуры / Структуры данных / Структура входных параметров индикатора
Документация по MQL5: Константы, перечисления и структуры / Структуры данных / Структура входных параметров индикатора
  • www.mql5.com
каждого элемента этого массива указывает тип данных, передаваемых данным элементом. Сами значения параметров индикатора необходимо предварительно поместить в соответствующие поля каждого элемента (в...
 

ну да, передавать 32 байта когда в 90% случаев достаточно 4-х... в общем, вопрос ветки не в этом, я интересуюсь с целью - быть может кто-то нашел более-менее изящное решение в описываемой ситуации


п.с. к тому же вопрос не в хранении в одном объекте/структуре данных разных типов, отнюдь

 
Ilya Malev:
схожий вопрос: почему при перегрузке метода (в сигнатуре перегруженного метода) не фигурирует тип возврата, а фигурируют только типы параметров. то есть нельзя определить два идентичных метода с разными типами возвращаемого значения. почему сделано такое ограничение? какой в этом смысл, почему нельзя сделать перегрузку метода по типу возвращаемого значения при идентичных параметрах

Метод не может решать, какой тип возвращать.

 
fxsaber:

Метод не может решать, какой тип возвращать.

Ну Вы другими словами повторили то, что я и написал. Вопрос был не в том, может или не может, а почему не может и как это изящно обойти

 
Ilya Malev:

Ну Вы другими словами повторили то, что я и написал. Вопрос был не в том, может или не может, а почему не может и как это изящно обойти

Контроль типов теряется. Посмотрите C++-ресурсы на предмет ответа на эти вопросы. Думаю, они задаются довольно часто.