4

Say I want to provide element-wise arithmetic operations operator+= and operator+ for std::vector to add vector entries element by element. Often, I see operator+ being implemented in terms of operator+= like so:

#include <algorithm>
#include <vector>

template<class Type>
std::vector<Type> & operator+=(std::vector<Type> &x, const std::vector<Type> &y) {
    // Checks for equal size of x and y omitted here...
    std::transform(std::begin(x), std::end(x), std::begin(y), std::begin(x), std::plus<Type>());
    return x;
}

template<class Type>
std::vector<Type> operator+(std::vector<Type> x, const std::vector<Type> &y) {
    // Checks for equal size of x and y omitted here...
    return x += y;
}

int main() {
    std::vector<double> v0{1.0, 2.0, 3.0};
    auto v1 = v0;

    auto v2 = v0;
    v2 += v0; // yields [2, 4, 6]

    auto v3 = v0 + v1; // yields [2, 4, 6]

    return 0;
}

In terms of performance, I guess

template<class Type>
std::vector<Type> operator+(const std::vector<Type> &x, const std::vector<Type> &y) {
    // Checks for equal size of x and y omitted here...
    std::vector<Type> result;
    result.reserve(x.size());
    std::transform(std::begin(x), std::end(x), std::begin(y), std::back_inserter(result), std::plus<Type>());
    return result;
}

is more efficient because it avoids initializing the copy of the first argument when entering the function but places the result directly into an uninitialized chunk of memory. Is it really worth it to implement the second version or can I assume the compiler to optimize anyway? Also, I consider the second alternative less generic than the first one. Imagine something like

#include <array>
#include <type_traits>

template<class Container, class Enable = void>
struct IsSequenceContainer: public std::false_type {
};

template<>
template<class Type, std::size_t size>
struct IsSequenceContainer<std::array<Type, size> >: public std::true_type {
};

template<>
template<class Type, class Allocator>
struct IsSequenceContainer<std::vector<Type, Allocator> >: public std::true_type {
};

// Use the following operations for std::array and std::vector
template<class Container>
typename std::enable_if<IsSequenceContainer<Container>::value, Container>::type operator+(Container x, const Container &y) {
    return x += y;
}
Marcel
  • 616
  • 1
  • 5
  • 15
  • Be careful if you provide element-wise addition for vectors - be sure that `operator+` isn't mistaken for a concatenation operator... – Toby Speight Dec 02 '15 at 09:20
  • There's also [std::valarray](http://en.cppreference.com/w/cpp/numeric/valarray), but it doesn't support erase, insert etc. – melak47 Dec 02 '15 at 09:26
  • @melak47 I am aware of `std::valarray` but I would actually prefer `std::vector` over `std::valarray`, see [this](http://stackoverflow.com/questions/1602451) post, for example. – Marcel Dec 02 '15 at 09:44
  • @TobySpeight I am aware of the ambiguity of `operator+=` and `operator+` with regard to containers. However, IMHO, as long as you clearly document your intent and depending on your application (e.g., scientific applications), it is perfectly legal to provide such operators, which might have a different meaning in a different context. – Marcel Dec 02 '15 at 09:47
  • 1
    Adding operator overloads for standard containers is problematic. It won't be found by ADL (since that only looks up `namespace std;`); and if you are in another with *any* definition of `operator+=` , it will hide this one. – M.M Dec 02 '15 at 10:26

2 Answers2

3

As with everything performance related: Profile the program, to see the what happens.

My guess is that the compiler will not optimize the code fully - and that it will probably never matter. The only way to know for sure is to try it out.

Implementing + in terms of += has the advantage that the two operations are known to be equivalent. This makes it less likely for a bug to occur. You should make sure that your optimization is necessary, before you give up this advantage. The idioms of C++ have usually become idioms for good reasons.

Jørgen Fogh
  • 7,516
  • 2
  • 36
  • 46
1

Have you had a look at std::valarray? It already provides those operation you need and you might benefit from SIMD. Which might be a performance++ for free.

TheROE
  • 134
  • 3