views:

632

answers:

3

I have a vector-like class that contains an array of objects of type "T", and I want to implement 4 arithmetic operators, which will apply the operation on each item:

// Constructors and other functions are omitted for brevity.
template<class T, unsigned int D>
class Vector {

public:
    // Add a value to each item: naive implementation.
    void operator += (const T&) {
        for (int i = 0; i < D; ++i) {
            data[i] += value;
        }
    }
    void operator -= (const T&) { ... }
    void operator *= (const T&) { ... }
    void operator /= (const T&) { ... }

private:
    T items[D];
};

Because operators will contain the same boilerplate code (looping over every element and applying appropriate operation), I thought I could generalize it:

template<class T, unsigned int D>
class Vector {

public:
    void operator += (const T& value) { do_for_each(???, value); }
    void operator -= (const T& value) { do_for_each(???, value); }
    void operator *= (const T& value) { do_for_each(???, value); }
    void operator /= (const T& value) { do_for_each(???, value); }

private:
    void
    do_for_each(std::binary_function<void, T, T>& op, T value) {
        std::for_each(data, data + D, std::bind2nd(op, value));
    }

    T data[D];
};

Now, the problem is, how do I pass an operator that takes two intrinsic types and returns void to do_for_each, as depicted in the example above? C++ does not let me do this trick for intrinsic types ("T::operator+=" will not work if "T" is "int").

+8  A: 

First, you should really return a reference from your operator+=, since you can later use them to implement operator+, operator- and so on. I will change that accordingly.

Also, your do_for_each has to be a template, since it has to know the precise type of the function object, as binary function objects are not polymorph classes. For the actual operation, you want to use std::transform:

template<class T, unsigned int D>
class Vector {

public:
    Vector& operator += (const T& value) { 
        do_for_each(std::plus<T>(), value); 
        return *this;
    }

    Vector& operator -= (const T& value) { 
        do_for_each(std::minus<T>(), value); 
        return *this;
    }

    Vector& operator *= (const T& value) { 
        do_for_each(std::multiplies<T>(), value);
        return *this; 
    }

    Vector& operator /= (const T& value) { 
        do_for_each(std::divides<T>(), value); 
        return *this;
    }

private:
    template<typename BinFun>
    void do_for_each(BinFun op, const T& value) {
        std::transform(data, data + D, data, std::bind2nd(op, value));
    }

    T data[D];
};

std::transform will just pass each element to the function object, and assigns the result back to the iterator given as the third argument.

Johannes Schaub - litb
I was just in the process of writing the exact same thing! :) Cheers, +1!
Drew Hall
You should return a const reference from the operators, so that doing this (which is really hard to read/figure out) is not possible: (v += v2) -= v3
SoapBox
SoapBox: i don't see particular reasons to forbid that. Also, when you return non-const, you do what most don't do. This strives against principle of least surprise. It's perfectly reasonable to do f(a += b); for example.
Johannes Schaub - litb
Johannes Schaub - litb
+2  A: 

You should really have a look at Boost Operators, a header-only library that really simplifies creating orthogonal and consistent arithmetic operator overloads.

Specifically: you might find that deriving from boost::operators::integer_arithmatic<T> saves you a lot of the repetition of this class.

Alastair
+1  A: 

I think litb is on the correct track and answered the exact question.
But I think this is the wrong solution.

I would rather not use the do_for_each() but rather use the std::transform() directly:

template<class T, unsigned int D>
class Vector 
{

  public:
    Vector& operator += (const T& value) { 
        std::transform(data, data + D, data, std::bind2nd(std::plus<T>(),value)); 
        return *this;
    }

    Vector& operator -= (const T& value) { 
        std::transform(data, data + D, data, std::bind2nd(std::minus<T>(),value)); 
        return *this;
    }

    Vector& operator *= (const T& value) { 
        std::transform(data, data + D, data, std::bind2nd(std::multiplies<T>(),value));
        return *this; 
    }

    Vector& operator /= (const T& value) { 
        std::transform(data, data + D, data, std::bind2nd(std::divides<T>(),value)); 
        return *this;
    }

  private:
    T data[D];
};
Martin York