1

I would like to implement a unary arithmetic operator with move semantic for std::vector. The purpose is to avoid internal allocation inside the operator if it is applied on an rvalue.

The problem: the following operation does not compile: c = -(a + b)

The reason this does not compile is because the binary arithmetic operator + I implemented for std::vector returns a const lvalue (so that the compiler will complain on (a + b) = c, which doesn't make sense).

The binary + operator

template<class T, class AllocVect1, class AllocVect2>
inline const std::vector<T> operator+ (
    const std::vector<T, AllocVect1> &v1,
    const std::vector<T, AllocVect2> &v2) {
    std::vector<T> vout;

    *Compute vout = v1 + v2*

    return vout;
}

The unary operator

template<class T, class Alloc>
inline const std::vector<T>& operator- (std::vector<T, Alloc> &&v) {
    std::transform (
        v.begin (), 
        v.end (), 
        v.begin (), 
        [](const auto &val){return -val;});
    return v;
}
Yaniv
  • 11
  • 2

1 Answers1

0

Pass and return your vector by value.

What may sound unintuitive, makes sense once you consider these things:

  1. A temporary object can be implicitly moved into a value parameter -- no copy occurs. This is as if you would call std::move on a local variable.

  2. This object (the parameter) can be modified in-place, since it has no const qualifier.

  3. The modified object can be returned, and move semantics are again applied implicitly. You should never return references to local variables -- neither lvalue nor rvalue references.

template<class T>
std::vector<T> operator-(std::vector<T> v) { // 1.
    std::for_each( // 2.
        v.begin(), 
        v.end(), 
        [](T& val) {val = -val;});

    return v; // 3.
}

This operator can be called with both rvalues (move + modify + move) and lvalues (copy + modify + move).

This being said, there are advantages of separate overloads for const Param& and Param&&, the details of which are already explained in separate answers:

TheOperator
  • 5,936
  • 29
  • 42
  • Thank you for your thorough explanation! – Yaniv Apr 24 '19 at 19:35
  • I do wonder however what will happen in the following assignment: auto c = -(a + b);? The operator+ returns a const lvalue so that programmers will not be tempted to write something like: (a + b) = 'some other vector'. But, since this operator+ returns a const lvalue, will it copy or move when calling the operator- you wrote in your answer? – Yaniv Apr 24 '19 at 19:41
  • `(a + b) = 'some other vector'` -- is this _really_ a use case worth protecting for? How often have you seen such code? – TheOperator Apr 24 '19 at 22:12
  • If you *also* fix `operator+` to return by value instead of const lvalue, everything will work -- you can't assign to an unnamed temp (so `(a + b) =`.. is disallowed), AND `-(a+b)` will "just work" without extra copies. – Chris Dodd Apr 24 '19 at 23:02
  • For primitive type (a+b) = 'something' issues the compiler error: lvalue required as left operand of assignment. If only there was something similar for general classes with defined overloads of the operator+ – Yaniv Apr 25 '19 at 14:58