Operator Overloading
Practice
Section titled “Practice”Implement a Vector2 type with 2 float fields in a separate module (Vector2.h and, if needed, Vector2.cpp).
Vector2 must be a plain struct with public fields.
Overload the following operators, where v and w are vectors and a is a float:
v * aa * v(the same asv * a)v *= av / av /= av + wv - wv += w(with support for the syntax(v += w) += w)v -= w- Make
std::cout << vprint the components ofvin parentheses, separated by a comma v[i]to obtain a reference to a component (iis 0 or 1).
- Overload them as free
inlinefunctions insideVector2.h; - Leave only the declarations of the overloaded operators in
Vector2.h, and put the definitions in acppfile; - Instead of free functions, use methods declared in the struct and defined in a
cppfile (where possible). Try to modify the function prototypes and bodies using textual replacement in your text editor (search-and-replace).
In main.cpp, test the functionality with asserts.
Key ideas
Section titled “Key ideas”-
Why is the
v * woperator a bad idea?Answer
It could mean either the dot product or component-wise multiplication (the Hadamard product). It is not obvious which operation
v * wwould perform. -
How can you call a particular operator as a function?
-
What is a “fluent interface”? How can support for it be added to the
<<,+=,-=, and similar operators?Note
A fluent interface can be created without overloading operators. Methods can be used as well.
For example, with your own
Vector2type, you could do this:struct Vector2{// ...public:Vector2& add(Vector2 v){*this = *this + v;return *this;}Vector2& sub(Vector2 v){*this = *this - v;return *this;}Vector2& scale(float a){*this = *this * a;return *this;}Vector2& printInto(std::ostream& out){// ...return *this;}};void usage(){Vector2 val{1, 2};val.add({ 1, 2 }).sub({ 2, 3 }).printInto(std::cout) // (0, 1).scale(5).add({ 1, 2 }).printInto(std::cout); // (1, 7)val.printInto(std::cout); // (1, 7)}For example, for printing, the language designers could have made an interface like this instead of
<<:std::cout.print(v).print(std::endl).print(w).print(std::endl);The only drawback is that it would not be possible to overload
printfor your own type without an additional overloading mechanism, such as requiringVector2to have a method with a specific name or aprintmethod with a particular signature (printwould need to invoke the overloaded logic, for example through static polymorphism using atemplate).