I'm doing my own array template version of an array.
I'm having some problems overloading a method.
I'm doing a specifik version of a "print()" if the type of array is <char>,(i'm saving bools as char).
this works without problems. I just needed to add a templete<> infront of my scpeciales "print()". This is shown at line 32-47.


Now I need to do an array version of division.
The general case is an array of floats, this will work without problem
So given normal Array with floats, my general version line 51-62 works.

But if I use a array<int>, i don't want it to return a array<int>, it should return a array<float>. (like float division)

But I'm having problems with the defintion.
Can someone give me a solution, thanks.

#include <iostream>

template<typename T>
class Array {
 public:
  Array() {puts("empty constructor");data_=NULL;x_=0;}
  Array(int length):x_(length),data_(new T[length]){}
  void init(int length) {x_=length;data_=new T[length]; }
  Array(const Array<T>& var);
  ~Array(){delete [] data_;}
  int length() const                  { return x_;     }
  T& operator() (uint r); 
  T operator() (uint r) const; 

  void print(int x=0, char sep=' ');
  void fillUp(T var) {for(int i=0;i<x_;i++) data_[i]=var;}
  void plus(const Array<T>&);
  Array<char> operator< (const float &f);
  Array<char> operator> (const float &f);
  Array<char> operator== (const float &f);
  int numTrues() const {return numOnes_;}

  Array<T>& operator= (const Array<T>&);
  Array<T> operator+ (const Array<T>&);
  Array<T> operator/ (const float &other);

private:
  int x_;
  T*  data_;
  int numOnes_;
};

template<typename T>
void Array<T>::print(int x,char sep){
  printf("printing array with dim=%d\n",x_);
    for(int i=0;i<x_;i++) 
      std::cout << data_[i] << sep;
  std::cout <<endl;
}

template<>
void Array<char>::print(int x,char sep){
  printf("printing array with dim=%d\n",x_);
    for(int i=0;i<x_;i++) 
      std::cout <<(int) data_[i] << sep;
  std::cout <<endl;
}



template<class T>//general
Array<T>  Array<T>::operator/ ( float const & other ){
  if(other==0){
    puts("ohh my god, divide by zero thats stupid, will exit");
    exit(0);
  }
  Array<T> tmp = Array<T>(x_);
  for(int i=0;i<x_;i++) 
    tmp.data_[i] = data_[i] / other;
  return tmp;
}

template<>//int specs
Array<float>  Array<int>::operator/ ( float const & other ){
  if(other==0){
    puts("ohh my god, divide by zero thats stupid, will exit");
    exit(0);
  }
  Array<float> tmp = Array<float>(x_);
  for(int i=0;i<x_;i++) 
    tmp.data_[i] = (float))data_[i] / other;
  return tmp;
}

Recommended Answers

All 7 Replies

>But if I use a array<int>, i don't want it to return
>a array<int>, it should return a array<float>.
Overloads can't differ only by return type. If you want an object of Array<float>, copy your Array<int> to it, then do the division. That's much easier and cleaner than the hoops you would have to go through to get exactly what you want.

>puts("ohh my god, divide by zero thats stupid, will exit");
>exit(0);
Terminating the program from a library class is equally stupid. I'd recommend throwing an exception instead. That way the application using your Array class can attempt to recover from an eminently recoverable error. Failure to support decent error handling results in nobody wanting to use your classes.

>But if I use a array<int>, i don't want it to return
>a array<int>, it should return a array<float>.
Overloads can't differ only by return type.....

They don't differ by return type,
They differ by the array calling the operator.
that is

Array<float> fArray(10);
fArray returnArray = fArray / 2.0;
vs
Array<int> iArray(10);
iArray returnArray = iArray / 2.0;

What you are suggesting is of cause a nice and easy work around.
But it doesn't solve the general problem.
It there is one, that is.

>
>puts("ohh my god, divide by zero thats stupid, will exit");
>exit(0);
Terminating the program from a library class is equally stupid. I'd recommend throwing an exception instead. That way the application using your Array class can attempt to recover from an eminently recoverable error. Failure to support decent error handling results in nobody wanting to use your classes.

I know I know,
but exception handling and catching, I always do when the program actually works.

Thanks for the reply

>They don't differ by return type,
The return type doesn't match the declared return type (Array<T>) and the return type is the only difference between the two definitions, therefore the definition is malformed. You can't win an argument with the compiler; I've tried, and it doesn't work.

>They differ by the array calling the operator.
As far as the compiler is concerned, Array<int> is simply a more specific version of Array<T> and will try to match the declaration using that type. Since you said originally that operator/ returns an Array<T>, then when T is int, the compiler expects to see a return value of Array<int>. That's all there is to it.

>But it doesn't solve the general problem.
In my opinion the general problem is a design flaw on your part. You're trying to get behavior that, while I can see your reasoning and can admit to its convenience, is unconventional and ultimately breeds confusion and horrible workarounds in the cases where users don't want a conversion from Array<int> to Array<float> during division.

>but exception handling and catching, I always do when the program actually works.
Then your definition of "works" is different from mine. Error handling is an integral part of program correctness.

>
>They differ by the array calling the operator.
As far as the compiler is concerned, Array<int> is simply a more specific version of Array<T> and will try to match the declaration using that type. Since you said originally that operator/ returns an Array<T>, then when T is int, the compiler expects to see a return value of Array<int>. That's all there is to it.

>But it doesn't solve the general problem.
In my opinion the general problem is a design flaw on your part. You're trying to get behavior that, while I can see your reasoning and can admit to its convenience, is unconventional and ultimately breeds confusion and horrible workarounds in the cases where users don't want a conversion from Array<int> to Array<float> during division.

Hmm, I'm quite new into this template,
and you are properly right, it's a bad design, and I guess the compiler is even more confused than I am.

But I still find it ackward, that there are no possibility to do it.
I can handle special cases when, I have different INPARS as long as the method is void.
But it's not possible to do it when you have a return type.

I do understand that there is no way around this, the compiler is always right.
But I don't see the rationale

>
>but exception handling and catching, I always do when the program actually works.
Then your definition of "works" is different from mine. Error handling is an integral part of program correctness.

I think it depends on your programming skills.
I end up sacking around 40-50 % of my code,
and this is within the first week.
I guess I play around and learn new stuff.

Whenever I know a method/function will survive I of cause do error handling.
And if ever I become an elite programmer I will of cause do it evenmore.
thanks for your reply

>But I still find it ackward, that there are no possibility to do it.
You can do it, just not with the precise syntax that you want. Due to the syntactic sugary nature of operator overloading, you gain some frustrating restrictions. Here are two simple options for working around the problem:

1) Don't use operator overloading. Instead, support a list of non-member template functions that extend and enhance your basic Array class. If you have an especially common return type for an operation, you can add that explicitly as an operator:

#include <iostream>

template <typename T>
class Array {};

template <typename T>
Array<double> operator/ ( const Array<T>& lhs, double rhs )
{
  std::cout<<"Array<double> operator/ ( const Array<T>& lhs, double rhs )\n";
  return Array<double>();
}

template <typename Dst, typename Src>
Array<Dst> div ( const Array<Src>& lhs, double rhs )
{
  std::cout<<"Array<Dst> div ( const Array<Src>& lhs, double rhs )\n";
  return Array<Dst>();
}

int main()
{
  Array<int> a;
  Array<int> b = div<int> ( a, 2 );
  Array<double> c = a / 2;
}

2) Don't try to overload on the return type. Simply assume that the result of division is an Array<double> object and be done with it:

#include <iostream>

template <typename T>
class Array {};

template <typename T>
Array<double> operator/ ( const Array<T>& lhs, double rhs )
{
  std::cout<<"Array<double> operator/ ( const Array<T>& lhs, double rhs )\n";
  return Array<double>();
}

int main()
{
  Array<int> a;
  Array<int> b = a / 2;
  Array<double> c = a / 2;
}

>But I don't see the rationale
It was a design decision not to consider return types as significant in overloading. Could the language be changed to support it? Certainly. Will it? We'll just have to wait and see.

> But I still find it ackward, that there are no possibility to do it.
> I can handle special cases when, I have different INPARS as long as the method is void.
> But it's not possible to do it when you have a return type.

you can get the effect of overloading on the return type by using a meta-function (array_divide_result_traits in the following snippet) to deduce the return type.

template<typename T> class Array ;

template< typename T > struct array_divide_result_traits
{
  typedef T element_type ;
  typedef Array<T> result_type ;
};

template<> struct array_divide_result_traits<int>
{
  typedef double element_type ;
  typedef Array<double> result_type ;
};

template<typename T>
class Array {
 public:
  // ...
  Array(int length):x_(length),data_(new T[length]){}

  template< typename U > Array( const Array<U>& that ) 
    : x_(that.x_), data_(new T[that.x_])
  { for( int i=0 ; i<x_ ; ++i ) data_[i] = T( that.data_[i] ) ; }
  // ...

  typedef typename array_divide_result_traits<T> divide_traits ;
  typename divide_traits::result_type operator/ (const double &other);

private:
  int x_;
  T*  data_;
  int numOnes_;
  template< typename U > friend class Array ;
};

template<typename T> 
typename Array<T>::divide_traits::result_type 
  Array<T>::operator/ ( double const & other )
{
  if(other==0){ /* error */ }

  typedef typename Array<T>::divide_traits::result_type result_type ;
  typedef typename Array<T>::divide_traits::element_type element_type ;

  result_type tmp = result_type(x_);

  for(int i=0;i<x_;i++) 
    tmp.data_[i] = element_type( data_[i] ) / other;

  return tmp;
}

int main()
{
  Array<int> a(10);
  Array<double> b = a / 2;
  Array<int> c = b ;
}

note 1: the templated copy constructor is there to make line 57 possible (though not very efficiently).
note 2: instead of creating a specialization for just an int, consider using the specialization for all integral types (use std::tr1::type_traits).

Thanks for your help.
I gues I just go with return double always.
I can see from vijayan121 response,
that the solution is not quite as straight forward as I had hoped.

thanks again.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.