lilburne wrote:
obviously this forces users to write their code in a functional style
rather than by using operator shorthands, the reasoning being that the
class user is never left in any doubt about whether something is going
to occur in place or not.
So we sidestep the issue of temporaries by saying
"if you require a temporary then you actively create one
yourself, you don't rely on the compiler doing it for
you."
this is a style of coding which we carry through with all our data
structures.
I agree that is a safe approach.
I am however, trying to capture the essence of object orientation which
urges user-defined types to behave like built-ins.
That is if:
double x,y,z;
x = y + z;
y /= z;
are valid for doubles, then
TMatrix<double> X,Y,Z;
X = Y + Z;
Y /= Z;
should also be (without surprises and bad side effects -- the
responsibility of the designer).
To this end, I have the following global templates:
//---------------------------------------------------------------------------
template<class TClass> const TClass operator+(const TClass &Left,const
TClass &Right)
{
return TClass(Left) += Right;
}
//---------------------------------------------------------------------------
template<class TClass> const TClass operator-(const TClass &Left,const
TClass &Right)
{
return TClass(Left) -= Right;
}
//---------------------------------------------------------------------------
template<class TClass> const TClass operator*(const TClass &Left,const
TClass &Right)
{
return TClass(Left) *= Right;
}
//---------------------------------------------------------------------------
template<class TClass> const TClass operator/(const TClass &Left,const
TClass &Right)
{
return TClass(Left) /= Right;
}
//---------------------------------------------------------------------------
These are always implemented as op= (per Scott Meyers). To support
matrix operations with these template, my dilemma is solved, since I
have had to redefined operator/= with a const parameter, and since the
parameter is decomposed internally, I must necessarily use a temporary:
template<class Type> TMatrix<Type>& TMatrix<Type>::operator/=(const
TMatrix &B)
{
// Division is strictly inversion and pre-multiplication.
TMatrix A(B);
A.PLU();
A.UFSub(*this);
A.BSub(*this);
return *this;
};
So, in this scenario I can match the behaviour of the built-ins (without
surprises), but I have not lost the ability to operations without
temporaries:
TMatrix f,K,x;
f = ...
K = ...
----------
x = f; // Contrived, but f not lost.
x /= K; // Uses temporary for K.
----------
OR
----------
f /= K; // Uses temporary for K, x not needed, but f modified
---------- // (no surprise)
OR
----------
K.Inverse(); // Very slow but OK.
x = K*f; // Solution, but non-intuitive if you did not spot the //
Inverse()
----------
OR
---------- // Fastest!
K.PLU(); // In-place decomposition - no surprise or temporary
K.UFSub(f);
K.BSub(f); // Solution in f
----------
OR
----------
x = f; // Contrived, but f not lost.
K.PLU(); // In-place decomposition - no surprise or temporary
K.UFSub(x);
K.BSub(x); // Solution in x.
----------
The last two could be wrapped in functions:
void Solve(TMatrix &f,const TMatrix &K);
TMatrix& Solve(const TMatrix &f,const TMatrix &K);
respectively.
Having given this a lot of thought recently, built-in behaviour can be
replicated by TMatrix operators which use temporaries. When performance
and memory storage are issues, use functions like x = Solve(f,K).
This strategy differs somewhat from yours in that when one wishes to use
the extremely convenient short-hand, one pays the price in temporaries,
but more traditional alternatives are available should the need arise.
Thanks for the input.
Regards
Andrew