Problems building a SquareMatrix template class - c++

I am trying to build a SquareMatrix template class by using a constructor that accepts 4 parameters as the 4 submatrices a, b, c, d occupying the four quadrants (a = northwest, b = northeast, c = southwest, d = southeast) of the matrix. Shown below:
template<class T> class SquareMatrix {
public:
SquareMatrix(){}
SquareMatrix(const T first, const T second, const T third, const T fourth) {
a = first;
b = second;
c = third;
d = fourth;
}
SquareMatrix<T>(const SquareMatrix<T>& rhs) { // copy constructor
a = rhs.getA();
b = rhs.getB();
c = rhs.getC();
d = rhs.getD();
}
SquareMatrix& operator=(const SquareMatrix rhs) { // assignment operator
if (&rhs != this) {
SquareMatrix(rhs);
}
return *this;
}
~SquareMatrix() {} // destructor
// getters and setters
T getA() const {return a;}
T getB() const {return b;}
T getC() const {return c;}
T getD() const {return d;}
void setA(const T& input) {a = input;}
void setB(const T& input) {b = input;}
void setC(const T& input) {c = input;}
void setD(const T& input) {d = input;}
private:
// 4 quadrants
// [a, b;
// c, d]
T a, b, c, d;
};
template<class T> SquareMatrix<T> operator+(const SquareMatrix<T> lhs,
const SquareMatrix<T>& rhs) {
SquareMatrix<T> ret(lhs);
ret.setA( ret.getA() + rhs.getA() );
ret.setB( ret.getB() + rhs.getB() );
ret.setC( ret.getC() + rhs.getC() );
ret.setD( ret.getD() + rhs.getD() );
return ret;
};
template<class T> SquareMatrix<T> operator-(const SquareMatrix<T> lhs,
const SquareMatrix<T>& rhs) {
SquareMatrix<T> ret(lhs);
ret.setA( ret.getA() - rhs.getA() );
ret.setB( ret.getB() - rhs.getB() );
ret.setC( ret.getC() - rhs.getC() );
ret.setD( ret.getD() - rhs.getD() );
return ret;
};
// this is the implementation of Strassen's algorithm
template<class T> SquareMatrix<T> operator*(const SquareMatrix<T>& lhs,
const SquareMatrix<T>& rhs) {
T product_1 = lhs.getA() * ( rhs.getB() - rhs.getD() );
T product_2 = ( lhs.getA() + lhs.getB() ) * rhs.getD();
T product_3 = ( lhs.getC() + lhs.getD() ) * rhs.getA();
T product_4 = lhs.getD() * ( rhs.getC() - rhs.getA() );
T product_5 = ( lhs.getA() + lhs.getD() ) * ( rhs.getA() + rhs.getD() );
T product_6 = ( lhs.getB() - lhs.getD() ) * ( rhs.getC() + rhs.getD() );
T product_7 = ( lhs.getA() - lhs.getC() ) * ( rhs.getA() + rhs.getB() );
SquareMatrix<T> ret;
ret.setA(product_5 + product_4 - product_2 + product_6);
ret.setB(product_1 + product_2);
ret.setC(product_3 + product_4);
ret.setD(product_1 + product_5 - product_3 - product_7);
return ret;
};
Now, I am trying to create a nested 4x4 matrix, by doing:
int main() {
cout << "Example: a 4x4 matrix: " << endl;
// 4 single quadrants
SquareMatrix<int> M_1A(1, 2, 3, 4);
SquareMatrix<int> M_1B(5, 6, 7, 8);
SquareMatrix<int> M_1C(9, 10, 11, 12);
SquareMatrix<int> M_1D(13, 14, 15, 16);
// 4x4 matrix M_1
SquareMatrix< SquareMatrix<int> > M_1(M_1A, M_1B, M_1C, M_1D);
// test
cout << "test: " << endl;
cout << M_1.getA().getA() << endl;
return 0;
}
The intended matrix output should be M_1 = [1,2,5,6; 3,4,7,8; 9,10,13,14; 11,12,15,16].
I use the M_1.getA().getA() command to first access M_1A and then access 1 nested inside it, but instead the output display a big number that constantly changes, perhaps an address? (last time I tried it yielded 6684672).
Is there a way to implement a matrix class in this manner?
(EDIT: now included assignment operator and destructor, likely sources of the bug)

As the comments suggested, it is the assignment operator that is faulty.
SquareMatrix& operator=(const SquareMatrix rhs) {
if (&rhs != this) {
SquareMatrix(rhs); // <-- This creates a temporary that
// dies off after that line is executed
}
return *this;
}
The assignment operator doesn't do any assignment. Instead a temporary SquareMatrix is made.
To fix this problem, either
1) Not supply any assignment operator, copy constructor, or destructor, since the type T should be safely copyable.
2) Fix the assignment operator so that it works correctly:
#include <algorithm>
//...
SquareMatrix& operator=(const SquareMatrix rhs) {
if (&rhs != this) {
SquareMatrix t(rhs);
std::swap(t.a, a);
std::swap(t.b, b);
std::swap(t.c, c);
std::swap(t.d, d);
}
return *this;
}
The assignment now works. However I suggest not writing code that need not be written, and your buggy implementation is the case in point.
In your case, if you let the compiler generate the assignment operator and/or rely on T in the template to have correct copy semantics, your class would have worked correctly.

Paul's comments are right on the mark. Although your SquareMatrix is not built-in, it is declared to be consisting of 4 elements of type T. The default copy c'tor for your class will be using the assignment operator or assignment c'tor of the actual type that T is representing in your usage.
I have some suggestions to improve the code:
If T is of a type that has a memory footprint that is bigger than a pointer/int: it is more efficient to let your c'tor receive the elements bij const reference like so:
SquareMatrix( const T& _a, const T& _b, const T& _c, const T& _d)
Use copy constructors as much as possible: that way the four elements don't get initialized first and then assigned later. Instead, they get initialized with the correct values at once.
SquareMatrix( const T& _a, const T& _b, const T& _c, const T& _d)
: a( _a), b( _b), c( _c), d( _d)
{ /* empty body */ }
Choose your names wisely to simplify things. Don't introduce extra mappings in name schemes when you don't have to; it just creates opportunities to slip. I already applied that in point 1 above :-).
Don't 'program by wishful thinking': writing in comments or type/variable names that something is supposed to be something will not make it that. In your case: your class IS NOT a square matrix, nor even a matrix. To the compiler it is a data type consisting of four elements, named a, b, c and d, respectively, of a type T to be defined at compile time.

Related

Changing type of template at run time

I'm trying to make a Matrix struct which would work with various data types, including my Complex struct:
struct Complex {
double re = 0, im = 0;
Complex operator*(const Complex& other) const {
return Complex(re * other.re - im * other.im, im * other.re + re * other.im);
}
Complex operator*(const double& other) const {
return Complex(re * other, im * other);
}
Complex() {}
Complex(double a) : re(a) {}
Complex(double a, double b) : re(a), im(b) {}
};
std::ostream& operator<<(std::ostream& out, Complex z) {
out << z.re << " " << z.im << "i";
return out;
}
template <typename T>
Complex operator*(const T& c, const Complex& z) {
return z * c;
}
The obvious way is to make a template like one in the code below:
template <typename T>
struct Matrix {
std::vector<T> m;
unsigned int rows, cols;
Matrix<Complex> operator*(const Complex& z) const {
Matrix<Complex> result(rows, cols);
for (int i = 0; i < m.size(); i++) {
result.m[i] = m[i] * z;
}
return result;
}
void operator*=(const Complex& z) {
(*this) = (*this) * z; // <- ideally we're trying to get this to work
}
void operator=(const Matrix<T>& other) {
rows = other.rows;
cols = other.cols;
m.resize(rows * cols);
m = other.m;
}
Matrix(const unsigned int& rows, const unsigned int& cols) : rows(rows), cols(cols) {
m.resize(rows * cols);
}
Matrix(const Matrix<T>& other) : rows(other.rows), cols(other.cols) {
(*this) = other;
}
};
int main() {
Complex z(1, 1);
Matrix<double> A(1, 1);
A.m[0] = 2.0;
std::cout << (A * z).m[0] << std::endl; // works as it should because a temporary Matrix<Complex> gets created
A *= z; // and here we're introducing the problem
std::cout << A.m[0] << std::endl;
}
The problem arises when calling *= operator. We're trying to call an unexisting = operator overload. My first attempt was to write something like this instead:
template <typename T_other>
void operator=(const Matrix<T_other>& other) {
rows = other.rows;
cols = other.cols;
m.resize(rows * cols);
for (int i = 0; i < m.size(); i++) {
m[i] = other.m[i];
}
}
This however leads to other problems:
The type of A is still Matrix<double> and after the multiplication it should be Matrix<Complex> to store complex numbers.
There is no conversion from Complex to double as it results in loss of data (the imaginary part).
Also, I would like to avoid creating a template specialization for Matrix<Complex>, but if there's no other way I'll accept it.
C++ is statically typed. Once you declare a variable and type, you can't change the type of that variable.
template <typename T>
struct Matrix {
void operator*=(const Complex& z) {
(*this) = (*this) * z;
}
}
The *= operator overload for your Matrix doesn't make sense. A Complex can hold the value of a double with imaginary part 0, but a double can never hold the value of a Complex.
Multiplying a real matrix by a complex number necessarily produces a complex matrix. So you try and assign the complex RHS to the real LHS - and either that makes no sense and shouldn't be done, or you have some idea for how to convert that (e.g. keep real part, keep absolute value etc) and then have to implement a Matrix<double> constructor from Matrix<Complex>.
Real numbers are a subset of complex numbers, so just make A a Matrix<Complex> from the beginning if you ever want to make it complex later.

How to apply overloaded polymorphed function on elements of base class pointer vector

I have a base class Object:
struct Object{
};
and n (in this case 2) classes that inherit from this
struct Integer : public Object{
int i_;
Integer(int i) : i_{i}{}
}
struct Float : public Object{
float f_;
Float(float f) : f_{f}{}
}
By (ab-)using polymorphism I can now store those two types in a vector:
std::vector<Object*> object_list{new Integer(1), new Float(2.1), new Integer(3), new Float(4.2)};
But now I would like to add all those values together.
I can think of...
1) ...defining functions
Integer* add(Integer* i, Integer* j);
Float* add(Integer* i, Float* f);
Float* add(Float* f, Float* g);
Float* add(Float* f, Integer* i);
But this would require to dynamically cast Object to all available types - twice, which seems like a catastrophe if I have enough children classes.
2) ... Templates, but that won't work, because the types are not known at compile time.
So what is the most efficient way regarding the following requirements:
*Execution time is more important than memory usage (although it should run on an 8GB system)
*It should support an arbitrary number of child classes, but must at least up to 20
*Is not limited to adding, but an arbitrary function f(Object* a, Object* b) should be supported
*The design of the classes is not yet fixed. If something works that requires change (or changing the total structure in it self) that is possible
*All possible types are known upfront, external DLLs do not need to be supported
*Does not need to support multiple inheritance
*Does not need to be robust in error handling. Recoverable would be nice but I can live with a SEGFAULT.
using Object = std::variant<Float, Integer>;
now you can have a std::vector<Object> and store Floats and Integers in it.
struct Integer {
int val = 0;
friend std::ostream& operator<<( std::ostream& os, Integer const& obj ) {
return os << obj.val;
}
};
struct Float {
double val = 0.;
friend std::ostream& operator<<( std::ostream& os, Float const& obj ) {
return os << obj.val;
}
};
using Object = std::variant<Integer, Float>;
std::ostream& operator<<( std::ostream& os, Object const& obj ) {
// note: if the type in Object doesn't have a << overload,
// this will recurse and segfault.
std::visit( [&]( auto const& e ){ os << e; }, obj );
return os;
}
Integer add_impl(Integer const& i, Integer const& j) { return {i.val + j.val}; }
Float add_impl(Integer const& i, Float const& j) { return {i.val + j.val}; }
Float add_impl(Float const& i, Float const& j) { return {i.val + j.val}; }
Float add_impl(Float const& i, Integer const& j) { return {i.val + j.val}; }
Object add( Object const& lhs, Object const& rhs ) {
return std::visit( []( auto& lhs, auto& rhs )->Object { return {add_impl( lhs, rhs )}; }, lhs, rhs );
}
Test code:
Object a = Integer{7};
Object b = Float{3.14};
Object c = Integer{-100};
Object d = Float{0.0};
std::cout << add( a, b ) << "," << add( b, c ) << "," << add( c, d ) << "," << add( add(a, b), add( c, d ) ) << "\n";
this implements a dispatch table (more recent compilers will generate a far more efficient one) that will look for add overloads.
The return type is an Object but it will contain either a Float or an Integer at runtime.
The list of types you support needs to be at one spot, at the definition of Object. These objects don't have to be related types.
You can extend the add_impl in the namespace of the types in Object instead of in a central location. ADL will be used to find the overload set.
Of course, I'd implement operator+ instead of add.
There are some tricks you can use to fix:
// note: if the type in Object doesn't have a << overload,
// this will recurse and segfault.
that problem; basically something like:
namespace ObjectOnly {
struct Object;
struct Object:std::variant<Integer, Float> {
using std::variant<Integer, Float>::variant;
std::variant<Integer, Float> const& base() const& { return *this; }
std::variant<Integer, Float> & base()& { return *this; }
std::variant<Integer, Float> const&& base() const&& { return std::move(*this); }
std::variant<Integer, Float> && base()&& { return std::move(*this); }
};
Object add_impl( Object const& lhs, Object const& rhs ) {
return std::visit( [](auto& lhs, auto& rhs)->Object { return {lhs+rhs}; }, lhs.base(), rhs.base() );
}
Object operator+( Object const& lhs, Object const& rhs ) {
return add_impl( lhs, rhs );
}
std::ostream& stream_impl( std::ostream& os, Object const& obj ) {
std::visit( [&]( auto const& e ){ os << e; }, obj.base() );
return os;
}
std::ostream& operator<<( std::ostream& os, Object const& obj ) {
return stream_impl( os, obj );
}
}
this will block add_impl from being able to see ObjectOnly::operator+. It will still be able to see operator+ in the same namespace as Float or Integer.
See here. If you edit Integer to not support << you'll get a compile-time instead of run-time error.
If you can choose a single type as the canonical "common" type, and provide a conversion from polymorphic types to that common type, then you can use that as the final and intermediary result of the sum.
For your example classes, a float object could be used to represent their value:
struct Object{
operator float() = 0;
};
Then, you can calculate the sum with a loop:
float sum = 0;
for (Object* o : object_list) {
sum += *o;
}

Operator overloading + operator in C++. How to overload objecttypeA+ObjectypeA = Objectype B?

I have two classes. Class OnePoint, and Class Line.
A OnePoint consists of a point, two coordinates.
A Class Line consists of two points, two OnePoint objects.
How can I add two OnePoints so that it becomes a line with operator overloading?
OnePoint a(3.0, 3.0);
OnePoint b(1.0, 1.0);
Line d;
d = a+b;
cout << d;
becomes {(3.0,3.0),(1.0,1.0)}.
#include <iostream>
using namespace std;
class OnePoint {
private:
double xvalue;
double yvalue;
public:
OnePoint(double x = 0.0, double y = 0.0) {
xvalue = x;
yvalue = y;
}
friend ostream& operator<<(ostream& printh, OnePoint& cPoint) {
printh << "(" << cPoint.xvalue << ',' << cPoint.yvalue << ")";
return printh;
}
void Plus(OnePoint a) {
xvalue = xvalue + a.xvalue;
yvalue = yvalue + a.yvalue;
}
void Minus(OnePoint b) {
xvalue = xvalue + b.xvalue;
yvalue = yvalue + b.yvalue;
}
OnePoint Plustwo(OnePoint a) {
return (xvalue + a.xvalue, yvalue - a.yvalue);
}
void Change(double a, double b) {
xvalue += a;
yvalue += b;
}
void Print(OnePoint b) {
cout << xvalue << "," << yvalue << endl;
}
/*OnePoint operator-(OnePoint a) {
OnePoint temp;
temp.xvalue = xvalue + a.xvalue;
temp.yvalue = yvalue + a.yvalue;
return temp;
}
friend OnePoint operator+(OnePoint a, OnePoint b) {
OnePoint temp;
temp.xvalue = a.xvalue + b.xvalue;
temp.yvalue = a.yvalue + b.yvalue;
return temp;
}*/
};
class Line {
private:
OnePoint onevalue;
OnePoint twovalue;
public:
Line(OnePoint a, OnePoint b) {
onevalue = a;
twovalue = b;
}
/*OnePoint getonevalue() {
return onevalue;
}
OnePoint gettwovalue() {
return twovalue;
}*/
friend ostream& operator<<(ostream& print, Line& cLine){
print << "{"<< cLine.onevalue << ',' << cLine.twovalue << "}";
return print;
}
friend Line operator+(OnePoint a, OnePoint b) {
Line temp; // I have been trying
temp(a, b); //something here without luck
return temp;
}
};
-------------------------------------------------------------------------------
int main(){
OnePoint a(3.0, 3.0);
OnePoint b(1.0, 1.0);
Line d(a, b);
cout << a << endl;
cout << d << endl;
}
You can rewrite (in C++11) your operator + like
Line operator+(const OnePoint& lhs, const OnePoint& rhs)
{
return {lhs, rhs};
}
All you need to do - without changing anything - is
OnePoint a(3.0, 3.0);
OnePoint b(1.0, 1.0);
Line d(a,b);
If you really want to do
d = a + b;
then you need to supply an operator+() that accepts two arguments of type OnePoint, and returns a Line. The alternatives are doing it as a member of OnePoint
// definition of Line here
class OnePoint
{
public:
Line operator+(const OnePoint &) const;
// other member functions, etc
};
Line OnePoint::operator+(const OnePoint &rhs) const
{
Line retval(*this, rhs);
return retval;
}
or as a non-member
// definitions of Line and OnePoint here
Line operator+(const OnePoint &lhs, const OnePoint &rhs); // declaration, not definition
Line operator+(const OnePoint &lhs, const OnePoint &rhs)
{
Line retval(lhs, rhs);
return retval;
}
Obviously, in both cases above, I have assumed that the operator+()s have access as needed (e.g. to Line' constructor).
Note that, mathematically, what you are doing is backward. Points are not added using a+b syntax to get a line - a line is represented using a pair of points, not a summation. Instead, vectors are added to points to produce other points.
Your mistake is very trivial. Your Line class constructor is multiple parameter constructor and you need to construct Line object using two argument of OnePoint type.
I have corrected you code and you will get idea of how to construct object of class if constructor require one or more parameter.
friend Line operator+(OnePoint a, OnePoint b) {
Line temp(a,b); // Changed here
return temp;
}
you can also go through this link to learn basics about object construction
Do not override + to do that. It is exceedingly unexpected.
If you really need a clean syntax, use a named operator.
Here is a 12 line named operator library:
namespace named_operator {
template<class D>struct make_operator{make_operator(){}};
template<class T, char, class O> struct half_apply { T&& lhs; };
template<class Lhs, class Op>
half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}
template<class Lhs, class Op, class Rhs>
auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
-> decltype( invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}
then we define an actual named operator using the above library as follows:
struct line_to_tag {}; // tag type
static const named_operator::make_operator<line_to_tag> line_to; // name of operator
// action from operator:
Line invoke( OnePoint lhs, line_to_tag, OnePoint rhs ) {
return Line(lhs, rhs);
}
and now this works:
Point a, b;
Line l = a *lineto* b;
which I think is far more clear than a+b. (and yes, the named operator library is a bit confusing, but the code at point of use is very clear).

C++ Vector Operator Overloading

I am trying to find a simple example program that overloads the following operators of a mathematic vector.
Constructor // create
= (equals) // assign
+; -; +=; -= // add sub
*; /; *=; /= // multi divide
++; -- // plus minus
== // compare
>; >=
<; <=
[] // access a value
Cant seem to find any good simple tutorials. I emphasize the simple because I am only learning this stuff now. If someone could link me or even better program a simple overload for just one of the operators as an example would be incredible!
There are a few things to know when you write operators, which are not as often used with other functions.
The assign operators, for example, will return *this because you change the value of the vector:
class v {
public:
double x_, y_;
v& operator += (const v& rhs)
{
_x += rhs._x;
_y += rhs._y;
return *this;
}
};
Another interesting one, the pre ++ and post ++ are different only because of an unused parameter:
class v {
public:
double x_, y_;
v& operator ++ (); // ++v
v& operator ++ (int); // v++
};
The "equal" (assignment) is another one that is tricky when you use pointers. For a vector, it generally won't be a problem, but if you define a vector V and assign it to itself, you have to be careful:
class v {
public:
double x_, y_;
v& operator = (const v& rhs)
{
if(this != &rhs)
{
x_ = rhs.x_;
y_ = rhs.y_;
}
return *this;
}
};
In your case, the if() will most certainly not be useful, but think about doing something like this:
delete p_;
p_ = new foo;
p_->x_ = rhs.p_->x_;
If &rhs == this, then the delete p_ deleted the rhs pointer! That means accessing it on the 3rd line is a bug.
The rest should be easy enough to work with. The compare operators return bool and are const:
class v {
public:
double x_, y_;
bool operator == (const v& rhs) const
{
return x_ == rhs.x_ && y_ == rhs.y_;
}
};
Although, since C++20, you are expected to only declare the three way comparison operator <=> which allows the compiler to implement all the other comparison operators for you. This one returns a negative number (smaller: a < b), 0 (equal: a == b), or a positive number (larger: a > b).
I'm not sure what makes a vector bigger or smaller, I used the length from (0, 0) in this example:
class v {
public:
double x_, y_;
int operator <=> (const v& rhs) const
{
if(x_ == rhs.x_ && y_ == rhs.y_)
{
return 0;
}
return length() > rhs.length() ? 1 : -1;
}
};
Except for the [] operator. There are two versions of that one:
class v {
public:
// I would imagine you'd use an array but as a simple example...
double x_, y_;
double operator [] (int idx) const
{
return idx == 0 ? x_ : y_;
}
v_ref operator [] (int idx)
{
v_ref v(this, idx);
return v;
}
};
As you can see, the non-constant version of the [] operator returns a reference. This is necessary so you can write something like:
r[3] = 7.3;
r[3] returns that reference, then the assignment of the reference is called with 7.3 as the parameter. (Note that we should probably throw an error if you use 3 as the index when you only have 2 values: 0 and 1--this is not shown here)
class v_ref
{
public:
v *p_;
int i_;
v_ref(v *p, int i)
: p_(p), i_(i)
{
}
operator = (double q)
{
// again, I suppose you'd use an array instead!
if(i_ == 0)
{
p_->x_ = q;
}
else
{
p_->y_ = q;
}
}
};
Assuming you want some security, the vector pointer could make use of a reference counter so you know whether a main vector object gets deleted before all of its reference objects...
Another note: I would imagine that your constructor will allocate an array of double (or use an std::vector<double> type...) If you use new, remember to delete in the destructor and that's when the if() in the assignment operator is very important.

Overloading on R-value references and code duplication

Consider the following:
struct vec
{
int v[3];
vec() : v() {};
vec(int x, int y, int z) : v{x,y,z} {};
vec(const vec& that) = default;
vec& operator=(const vec& that) = default;
~vec() = default;
vec& operator+=(const vec& that)
{
v[0] += that.v[0];
v[1] += that.v[1];
v[2] += that.v[2];
return *this;
}
};
vec operator+(const vec& lhs, const vec& rhs)
{
return vec(lhs.v[0] + rhs.v[0], lhs.v[1] + rhs.v[1], lhs.v[2] + rhs.v[2]);
}
vec&& operator+(vec&& lhs, const vec& rhs)
{
return move(lhs += rhs);
}
vec&& operator+(const vec& lhs, vec&& rhs)
{
return move(rhs += lhs);
}
vec&& operator+(vec&& lhs, vec&& rhs)
{
return move(lhs += rhs);
}
Thanks to r-value references, with these four overloads of operator+ I can minimize the number of objects created, by reusing temporaries. But I don't like the duplication of code this introduces. Can I achieve the same with less repetition?
Recycling temporaries is an interesting idea and you're not the only one who wrote functions that return rvalue references for this reason. In an older C++0x draft operator+(string&&,string const&) was also declared to return an rvalue reference. But this changed for good reasons. I see three issues with this kind of overloading and choice of return types. Two of them are independent of the actual type and the third argument refers to the kind of type that vec is.
Safety issues. Consider code like this:
vec a = ....;
vec b = ....;
vec c = ....;
auto&& x = a+b+c;
If your last operator returns an rvalue reference, x will be a dangling reference. Otherwise, it won't. This is not an artificial example. For example, the auto&& trick is used in the for-range loop internally to avoid unnecessary copies. But since the life-time extension rule for temporaries during reference binding does not apply in case of a function call that simply returns a reference, you'll get a dangling reference.
string source1();
string source2();
string source3();
....
int main() {
for ( char x : source1()+source2()+source3() ) {}
}
If the last operator+ returned an rvalue reference to the temporary that is created during the first concatenation, this code would invoke undefined behaviour because the string temporary would not exist long enough.
In generic code, functions that return rvalue references force you to write
typename std::decay<decltype(a+b+c)>::type
instead of
decltype(a+b+c)
simply because the last op+ might return an rvalue reference. This is getting ugly, in my humble opinion.
Since your type vec is both "flat" and small, these op+ overloads are hardly useful. See FredOverflow's answer.
Conclusion: Functions with an rvalue reference return type should be avoided especially if these references may refer to short-lived temporary objects. std::move and std::forward are special-purpose exceptions to this rule of thumb.
Since your vec type is "flat" (there is no external data), moving and copying do exactly the same thing. So all your rvalue references and std::moves gain you absoutely nothing in performance.
I would get rid of all additional overloads and just write the classic reference-to-const version:
vec operator+(const vec& lhs, const vec& rhs)
{
return vec(lhs.v[0] + rhs.v[0], lhs.v[1] + rhs.v[1], lhs.v[2] + rhs.v[2]);
}
In case you have little understanding of move semantics yet, I recommend studying this question.
Thanks to r-value references, with these four overloads of operator+ I can minimize the number of objects created, by reusing temporaries.
With a few exceptions, returning rvalue references is a very bad idea, because calls of such functions are xvalues instead of prvalues, and you can get nasty temporary object lifetime problems. Don't do it.
This, which already works wonderfully in current C++, will use move semantics (if available) in C++0x. It already handles all cases, but relies on copy elision and inlining to avoid copies – so it may make more copies than desired, particularly for the second parameter. The nice bit about this is it works without any other overloads and does the right thing (semantically):
vec operator+(vec a, vec const &b) {
a += b;
return a; // "a" is local, so this is implicitly "return std::move(a)",
// if move semantics are available for the type.
}
And this is where you would stop, 99% of the time. (I am likely underestimating that figure.) The rest of this answer only applies once you know, such as through the use of a profiler, that extra copies from op+ are worth further optimization.
To completely avoid all possible copies/moves, you would indeed need these overloads:
// lvalue + lvalue
vec operator+(vec const &a, vec const &b) {
vec x (a);
x += b;
return x;
}
// rvalue + lvalue
vec&& operator+(vec &&a, vec const &b) {
a += b;
return std::move(a);
}
// lvalue + rvalue
vec&& operator+(vec const &a, vec &&b) {
b += a;
return std::move(b);
}
// rvalue + rvalue, needed to disambiguate above two
vec&& operator+(vec &&a, vec &&b) {
a += b;
return std::move(a);
}
You were on the right track with yours, with no real reduction possible (AFAICT), though if you need this op+ often for many types, a macro or CRTP could generate it for you. The only real difference (my preference for separate statements above is minor) is yours make copies when you add two lvalues in operator+(const vec& lhs, vec&& rhs):
return std::move(rhs + lhs);
Reducing duplication through CRTP
template<class T>
struct Addable {
friend T operator+(T const &a, T const &b) {
T x (a);
x += b;
return x;
}
friend T&& operator+(T &&a, T const &b) {
a += b;
return std::move(a);
}
friend T&& operator+(T const &a, T &&b) {
b += a;
return std::move(b);
}
friend T&& operator+(T &&a, T &&b) {
a += b;
return std::move(a);
}
};
struct vec : Addable<vec> {
//...
vec& operator+=(vec const &x);
};
Now there's no longer a need to define any op+ specifically for vec. Addable is reusable for any type with op+=.
I coded up Fred Nurk's answer using clang + libc++. I had to remove the use of initializer syntax because clang doesn't yet implement that. I also put a print statement in the copy constructor so that we could count copies.
#include <iostream>
template<class T>
struct AddPlus {
friend T operator+(T a, T const &b) {
a += b;
return a;
}
friend T&& operator+(T &&a, T const &b) {
a += b;
return std::move(a);
}
friend T&& operator+(T const &a, T &&b) {
b += a;
return std::move(b);
}
friend T&& operator+(T &&a, T &&b) {
a += b;
return std::move(a);
}
};
struct vec
: public AddPlus<vec>
{
int v[3];
vec() : v() {};
vec(int x, int y, int z)
{
v[0] = x;
v[1] = y;
v[2] = z;
};
vec(const vec& that)
{
std::cout << "Copying\n";
v[0] = that.v[0];
v[1] = that.v[1];
v[2] = that.v[2];
}
vec& operator=(const vec& that) = default;
~vec() = default;
vec& operator+=(const vec& that)
{
v[0] += that.v[0];
v[1] += that.v[1];
v[2] += that.v[2];
return *this;
}
};
int main()
{
vec v1(1, 2, 3), v2(1, 2, 3), v3(1, 2, 3), v4(1, 2, 3);
vec v5 = v1 + v2 + v3 + v4;
}
test.cpp:66:22: error: use of overloaded operator '+' is ambiguous (with operand types 'vec' and 'vec')
vec v5 = v1 + v2 + v3 + v4;
~~~~~~~ ^ ~~
test.cpp:5:12: note: candidate function
friend T operator+(T a, T const &b) {
^
test.cpp:10:14: note: candidate function
friend T&& operator+(T &&a, T const &b) {
^
1 error generated.
I fixed this error like so:
template<class T>
struct AddPlus {
friend T operator+(const T& a, T const &b) {
T x(a);
x += b;
return x;
}
friend T&& operator+(T &&a, T const &b) {
a += b;
return std::move(a);
}
friend T&& operator+(T const &a, T &&b) {
b += a;
return std::move(b);
}
friend T&& operator+(T &&a, T &&b) {
a += b;
return std::move(a);
}
};
Running the example outputs:
Copying
Copying
Next I tried a C++03 approach:
#include <iostream>
struct vec
{
int v[3];
vec() : v() {};
vec(int x, int y, int z)
{
v[0] = x;
v[1] = y;
v[2] = z;
};
vec(const vec& that)
{
std::cout << "Copying\n";
v[0] = that.v[0];
v[1] = that.v[1];
v[2] = that.v[2];
}
vec& operator=(const vec& that) = default;
~vec() = default;
vec& operator+=(const vec& that)
{
v[0] += that.v[0];
v[1] += that.v[1];
v[2] += that.v[2];
return *this;
}
};
vec operator+(const vec& lhs, const vec& rhs)
{
return vec(lhs.v[0] + rhs.v[0], lhs.v[1] + rhs.v[1], lhs.v[2] + rhs.v[2]);
}
int main()
{
vec v1(1, 2, 3), v2(1, 2, 3), v3(1, 2, 3), v4(1, 2, 3);
vec v5 = v1 + v2 + v3 + v4;
}
Running this program produced no output at all.
These are the results I got with clang++. Interpret them how you may. And your milage may vary.