views:

286

answers:

3

I know that it's a good idea to make as much of the interface of a class non-member non-friend as possible, and I've just realised that for my 3D vector class, 'Vector3', I can move the +=, -= and so on operators out of the class, leaving just constructors and the copy assignment operator.

The question is: what should this operator look like? I've seen canonical forms of plenty of other operators and have followed their advice, but I haven't seen canonical forms of these operators. I've given what I think it should be below.

The secondary question is: what are these operators even called? Arithmetic assignment operators?

The (relevant) code before:

class Vector3 {
public:
    Vector3& operator+=(const Vector3& rhs);
    float x, y, z;
};

Vector3& Vector3::operator+=(const Vector3 &rhs) {
    x += rhs.x;
    y += rhs.y;
    z += rhs.z;

    return *this;
}

What I've changed it to so far:

class Vector3 {
public:
    float x, y, z;
};

Vector3& operator+=(Vector3& lhs, const Vector3& rhs) {
    lhs.x += rhs.x;
    lhs.y += rhs.y;
    lhs.z += rhs.z;

    return lhs;
}
+1  A: 

What you have looks good to me.

By the way, when you come to the operator+, it is common to implement that in terms of +=. (create a copy of lhs, and then call lhs += rhs and return the result)

Don't know if you're already aware of this trick, but since you're concerned about canonical ways to implement these operators, it can't hurt to mention it. :)

jalf
Fair enough, I'll accept your answer since you got there two minutes before the other guys ;)I'm aware of it, but others reading this may not be, so thanks for mentioning it!
Ben Hymers
A: 

I wouldn't say "as much of the interface as possible". There isn't much to be gained by making operator+=, operator-= etc. a not-friend not-member.

Some people prefer to make every function possible a non-member non-friend function to resist the temptation of using private member variables. But you are an adult: you can write the function as a public member, and not use the private member variables. I prefer to know that the function is tied to the class, and making it a public member makes it explicit.

Aside note:
Personally, I find that it's often OK to use the private members instead of public accessors (I know -- I'll burn in hell). Not always! -- but often.
In fact, there are few cases where you couldn't make the switch to public accessors in a manner of minutes if you decide you need it. But I recognize it's not a popular view and I don't ask people to follow that philosophy.

There are concrete reasons to make certaint functions and operators "non-friend, non-member". For example, operator<<() when used as the "stream-insertion operator" cannot be made a member because you would have to change the lhs class (the stream).

Euro Micelli
In this case there are actually plenty of non-member functions already since sometimes it's useful to perform conversions on the types of the left-hand object, so if anything it looks more tidy clustering all functions outside of the class rather than having them split between member and non-member :) Good advice in general though, thanks.
Ben Hymers
I disagree. If the function or operator doesn't need access to private variables, why give it access? Yes, we can just choose not to access them when writing the function, but what later, when we're debugging the code? Or testing the class? We have to at least examine all member functions to see if they do anything nasty to private members. So if you have the option, why not keep the += operator cleanly outside the class? Where's the cost?
jalf
@jalf: yes, I'm with you. Totally; really, I promise. I hesitated posting that. But -- let me shock my friends and coworkers -- sometimes I can actually be pragmatic: these kinds of operators tend to be three-liners, the classes that use them tend to be "value types", there tends to be a half dozen of them, and they tend to be rather trivial. Then, in practice, it just doesn't matter. I'll rather focus on the page-plus-long methods.
Euro Micelli
+1  A: 

What you have looks good.

The basic way to think about this, intuitively, is to think about what you'd like code to look like when you write it. If, in this case, you can write

Vector v, w;

v += w;
w += v;

and so on, you're on the right track.

There are a lot of good rules of thumb to help; see this entry in the C++ FAQ for lots on it.

Charlie Martin