30

I've started learning C++, so I don't know in my lack of knowledge/experience why something so seemingly simple to a rookie as what I'm about to describe isn't already in the STL. To add a vector to another vector you have to type this:

v1.insert(v1.end(), v2.begin(), v2.end());

I'm wondering whether in the real world people just overload the += operator to make this less verbose, for example to the effect of

template <typename T>
void operator+=(std::vector<T> &v1, const std::vector<T> &v2) {
    v1.insert(v1.end(), v2.begin(), v2.end());
}

so then you can

v1 += v2;

I also have this set up for push_back to "+=" a single element to the end. Is there some reason these things should not be done or are specifically avoided by people who are proficient in C++?

Lukasz Wiklendt
  • 4,319
  • 2
  • 19
  • 16
  • 11
    I would guess it's not in the standard because it's a bit awkward - the way you've done it the value types of the vectors have to be identical, whereas actually you could append any container (or range) of any type that's convertible to the value type on the LHS. And that's what `insert` does already. Also there are two plausible meanings for addition of vectors - concatenation as you've done here, or pairwise addition like `valarray` does, requiring operands of equal size. So it's not an ideal candidate for operator overloading. – Steve Jessop Jun 16 '11 at 01:52
  • 12
    Not to mention that from the title I assumed you meant to perform `v[i] += v[i]` over all the elements. – dmckee --- ex-moderator kitten Jun 16 '11 at 01:56
  • Operator overloading makes sense in same particular cases, but frequently it leads to behavior that people find surprising. Behaviors that seem obvious to one don't seem so to another. Personally, I thought of appending when I saw the title of the question, and that's because I usually use Python, not C++. – Omnifarious Jun 16 '11 at 02:09
  • 1
    IMHO, one reason to not think of `operator+=()` as pairwise addition is because the purpose of a vector is to contain elements. Operating on the elements is what I would consider beyond the design/responsibility of vector. Based on this then by principle of least privilege `operator+=()` should be assumed to be vector insertion. – Lukasz Wiklendt Jun 16 '11 at 02:26
  • 2
    @lukasz: Obviously you've never used MatLab (or octave). – Ben Voigt Jun 16 '11 at 02:31
  • @BenVoigt : I think that's quite an unfair comparison; despite the names of the data types, `vector` is a fundamentally different data type in C++ than Octave or Matlab -- the former implements the computer science definition of 'vector' (a one-dimensional array) while the latter implement the algebraic definition of 'vector' (a one-dimensional matrix). Because they're fundamentally different, they should be expected to behave differently and to be used differently. – ildjarn Jun 16 '11 at 02:49
  • @ildjarn: Where do you think computer science got the word vector from in the first place? There's no fundamental difference. In fact, in MatLab a string is a vector of element type `char`. In many respects, computer science is algebra. – Ben Voigt Jun 16 '11 at 02:56
  • @BenVoigt : The fundamental difference is how one typically _uses_ a vector in these two domains. The origin of the word is irrelevant. – ildjarn Jun 16 '11 at 03:01
  • @ildjarn: You don't think anyone uses C++ for computation, storing numeric data in e.g. a `vector>`? – Ben Voigt Jun 16 '11 at 03:10
  • @BenVoigt : Sure, but they're the minority compared to the rest of C++ users. – ildjarn Jun 16 '11 at 03:32
  • @ildjarn: And every single MatLab programmer falls into that minority (if they use C++ at all). Which directly implies my first comment. – Ben Voigt Jun 16 '11 at 03:36
  • @BenVoigt : And every single Matlab programmer who _cares about performance_ knows to not use `std::vector`. This is really a pointless debate. – ildjarn Jun 16 '11 at 03:40
  • @BenVoigt: I use Matlab for more than 60% of my work. I suppose one of the reasons I didn't stumble upon += as a pairwise element addition is because I'm not using vector for numerical data, but rather to store a list of objects which can't be added together. If I was going to use a container representing numerical elements I would probably use some kind of matrix library. However, I now understand that if you have something like `vector v1, v2;` and later `v1 += v2;` it could get ambiguous. – Lukasz Wiklendt Jun 16 '11 at 07:07
  • @Lukasz: Then surely you would expect `v1 + v2` to perform pairwise addition and return a new vector, right? `+=` is supposed to be related to `+`, but do the addition in place. – Ben Voigt Jun 16 '11 at 13:37
  • 1
    @Lukasz: 5 years later... This is an example of "lifting". You want to lift the (+) operator into the vector container. It's a very nice thing to be able to do. In Haskell, zipwith would the function you're looking for. It takes two lists and zips them together with a function. You could also write zipwith in C++. – Samuel Danielson Feb 29 '16 at 04:03

4 Answers4

28

This is actually a case where I would like to see this functionality in the form of an overload of append(). operator+= is kinda ambiguous, do you mean to add the elements of each vector to each other? Or you mean to append?

However, like I said, I would have welcomed: v1.append(v2); It is clear and simple, I don't know why it isn't there.

Evan Teran
  • 87,561
  • 32
  • 179
  • 238
  • 1
    @Mark: I agree, if you wish for such a functionality, then do it yourself! `template void append(L& lhs, R const& rhs) { lhs.insert(lhs.end(), rhs.begin(), rhs.end()); }` (with a bit of SFINAE to restrict L and R to containers). – Matthieu M. Jun 16 '11 at 08:45
  • @Matthieu should it not be `const R& lhs` instead of `R const& rhs`? – Lukasz Wiklendt Jun 16 '11 at 10:55
  • @Lukasz: no! That's an awful anglicism! I prefer putting the qualifiers after the type, because then it's easier to interpret what a `T const*` is, the `const` applies to what is before it (here), so better be consistent! – Matthieu M. Jun 16 '11 at 11:59
  • @Lukasz: The two are equivalent. – Ben Voigt Jun 16 '11 at 13:38
  • 1
    @MatthieuM. That append function would have to be called as `append(a,b)`, not `a.append(b)`, right? – Baruch Oct 21 '13 at 14:20
  • @baruch: yes, C++ does not support the concept of *extension methods* like C# does. – Matthieu M. Oct 21 '13 at 14:26
8

I think the main reason is that the semantics of += aren't obvious. There's the meaning that you have here, but there's also an equally valid meaning, which is element-wise addition of each element of equal-sized vectors. Due to this ambiguity I assume they decided it was better to rely on the user to call insert diretly.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • Also, as Steve Jessop said above, `insert()` is far more powerful as it operates on iterators. Why would you need anything else that's less useful? – Kerrek SB Jun 16 '11 at 02:41
  • 5
    @Kerrek: More powerful != more useful. Often, simpler is better. – Ben Voigt Jun 16 '11 at 03:12
6

Operators should be overloaded only when you are not changing the meaning of those operators.*

What that means is, for example, if there is no mathematical definition of the product of two objects, don't overload the multiplication operator for those objects. If there was a mathematical correspondence, operators overloading can make a class more convenient to use by allowing equations to be expressed in the form a*b + c rather than the more verbose (a.multiply(b)).add(c) Where addition and multiplication operators are used, addition and multiplication should be the intent. Any other meaning is more than likely to confuse others. Some other types where overloading operators is acceptable (and, in my opinion, preferable) are smart pointers, iterators, complex numbers, vectors, and bignums.

This follows from one of the design goals of C++, that it should be possible to define classes that are as easy to use as built in types. Of course there are operators you can define on classes which are not mathematical concepts either. You may wish to overload the == and != operators instead of writing an isEqual method, and might want to overload = because the compiler's default assignment operator is not what you want.

On the other hand, overloading an operator to do something unexpected, like defining ^ to translate a string to Japanese, is obscure and dangerous. Your fellow programmers will not be happy to find out that what looked like an exclusive or comparison was actually something very different. The solution then is to write your classes to make it easy to write clear and maintainable code, whether that means using operator overloading or avoiding it.

Adding two vectors is too ambiguous to warrent defining an operator. As others have shown, many people have different ideas of what this means, whereas for a string it is universally understood that adding two strings together means concatenation. In your example, it isn't entirely clear whether you want to do a component wise add on all the elements, add an element to the end, or concat two vectors together. Although it may be more concise to do it that way, using operator overloading to create your own programming language isn't the best way to go.

*Yes, I know Stroustrup overloaded << and >> to do stream operations rather than bitshifts. But those weren't used often compared to arithmetic and pointer operators in the first place, and it could be argued that now that everyone knows how to use cout, it's generally understood that << and >> are the inserter and extractor operators. (He originally tried to use just < and > for input and output, but the meaning of those operators was so ingrained in everyone's minds that the code was unreadable.)

Jeff Linahan
  • 3,775
  • 5
  • 37
  • 56
  • 2
    I agree with your principle about obviousness. However, if I gave you a piece of paper and said "add this to the pile of papers", you would know just what I meant. And if we were pair programming together and you had just written the code to make (and set values in) some Foo object and I said "now you need to add it to the vector of Foos" I don't believe for a moment that you would think to increase each Foo in the vector by the one you had just made. You would call push_back or insert, right? – Kate Gregory Jun 16 '11 at 20:26
  • If you said "add this Foo to the vector of Foos" yes I would understand what you meant, and I would call push_back, otherwise you should've said "Add Foo to every Foo in this vector." If someone were to instead say "add these two vectors together" I would think they want me to do a component wise add. If concating the vectors was what you want, I would've expected you to say something like "put everything in this vector onto the end of that vector." But that's just the thing, I don't feel += by itself is enough information to disambiguate. – Jeff Linahan Jun 17 '11 at 06:45
4

In addition to what others mentioned about this syntax not being intuitive and therefore error prone, it also goes against a good design rule making general algorithms applied to various containers free functions, and container specific algorithms -- member functions. Most containers follow this rule, except std::string, which got a lot of flack from Herb Sutter for its monolithic design.

Gene Bushuyev
  • 5,512
  • 20
  • 19
  • 2
    Speaking of which, I never like it when operator+() is not commutative (e.g. string concatenation). Something about that just feels wrong... – Nemo Jun 16 '11 at 05:01