user3548913
user3548913

Reputation: 1

adding arrays of different types (c++)

i need to dynamically create arrays containing different numerical types, including char, int, unsigned int, float, double. i'd like to be able to create any two of these arrays and, assuming they're of the same length, implement arithmetic operators such as operator+=

i've been able to implement the array as a template, but i don't know how to implement any arithmetic operators, since i can't know what the other array's type will be at compile time, or even when the first array is created (i will know the type for the array i'm constructing). i looked at std::array, but it doesn't support arithmetic operators. another alternative, which is definitely not elegant (but does work), is to implement a series of type specific operators, such as

MyArray<V> operator+ (const MyArray<float>& addend) const;
MyArray<V> operator+ (const MyArray<double>& addend) const;
MyArray<V> operator+ (const MyArray<int32>& addend) const;
MyArray<V> operator+ (const MyArray<int16>& addend) const;

thanks for any advice.

Upvotes: 0

Views: 197

Answers (3)

user2249683
user2249683

Reputation:

You will likely have to dispatch your operations by a result type selected by some type traits.

Simplified for a number (no vector):

#include <iostream>

template <typename T>
struct Number {
    T value;
    Number(T value) : value(value) {}

    template <typename U>
    explicit Number(U value) : value(value) {}

    operator T () const { return value; }
};

#define C_PLUS_PLUS_11 (201103L <= __cplusplus)

template <typename U, typename V>
struct binary_operation_traits {
    #if C_PLUS_PLUS_11
    typedef decltype(U() + V()) result_type;
    #endif
};

#if ! C_PLUS_PLUS_11
template <typename T>
struct binary_operation_traits<T, T> {
    typedef T result_type;
};

template <>
struct binary_operation_traits<int, float> {
    typedef float result_type;
};

template <>
struct binary_operation_traits<int, double> {
    typedef double result_type;
};

// And so on ...

#endif

template <typename U, typename V>
Number<typename binary_operation_traits<U, V>::result_type>
operator + (const Number<U>& a, const Number<V>& b) {
    typedef typename binary_operation_traits<U, V>::result_type result_type;
    return Number<result_type>(result_type(a) + result_type(b));
}

int main ()
{
    Number<int> a(1);
    Number<double> b(1.5);
    std::cout << a + b << '\n';
    return 0;
}

Upvotes: 0

IdeaHat
IdeaHat

Reputation: 7881

Alright, its probably obvious enough from my comments of everything in this thread that this is a particular sore spot for me. And this is for good reason, I was once like you. I was like, I can overload operators! AMAZING! OVERLOAD ALL THE OPERATORS (this was with a custom image container type). After a while, a few things became clear:

  1. Operators are hard to correctly declare, especially templated ones.
  2. Templated operators cannot have their types set explicitely, only implicitly.
  3. Operation order doesn't make sense all the time.
  4. Operators must either use exceptions as their "fail" mode which is not ideal in all cases, or use "enable-if" type syntax if the fail can be detected at compile time.
  5. Operator MEANING is hard to document/elucidate. Different interpretations of what an operator should "do" makes it hard to figure out. (Should the MyArray<T>+MyArray<J> work as a memberwise plus like T+J or should it work like a concatenate like 'string+string'?)
  6. Operators must return by value, which can cause overhead if your moves aren't all set up correctly/you aren't in C++11/for any reason return value elision doesn't happen.
  7. Overall, writing your own container types is a great way to redo alot of work the STL has already done.

You COULD do it like (at a namespace scope) (assuming you have a templated cast operator available)

template <typename T, typename J>
MyArray<decltype(T()+J())> operator+(const MyArray<T>& x,const MyArray<J>& y)
{
   using K=decltype(T()+J());
   MyArray<K> ret(x.size());//or something?
   for (size_t i = 0; i < x.size(); i++) {ret[i]=x[i]+y[i];}//could replace with foreach
   return ret;
};

Though using the following with vectors just makes more sense. You can wrap it in a "add" call if you want.

std::vector<T> a;//or whatever
std::vector<J> b;//or whatever
std::vector<K> c(a.size());//note: you can still use the decl type here, OR just define it to whatever you actually want it to be
std::transform(a.begin(), a.end(). b.begin(), c.begin(), std::plus<K>());

If you are trying to do this all over the place, and are trying to make a matrix math library, use one like Eigen, it'll save you a lot of work, it'll be strongly typed as a matrix and not a generic collection, and it'll be done with the full math knowledge the Eigen team has.

Upvotes: 1

Batuhan Tasdoven
Batuhan Tasdoven

Reputation: 798

You can use one more template parameter:

template<class V, class T> MyArray<V> operator+ (const MyArray<T>& addend) const;

Then the cast will always be according to your main array type.

Upvotes: 0

Related Questions