Consider this following class Operand
: It is a pretty straight forward class as it takes in a single Parameter and defines a bunch of overloaded operators and for simplicity of testing this class I chose to use a float
type since there are division operators. If you notice with some of the operators they are defined as friends so that one is able to do this in their code:
Edit Someone asked in the comments what does
inline friend Operand Operand::operator+( const Operand& A, const Operand B )
mean? It is an overloaded operator but since one is already defined for the class itself as a member this is declared as a friend to the class and is defined within the class but is not a class member. This allows the user to add two Operand class instances or objects together and returns back the newly calculated value as an Operand object. I'll add comments to the snippet
section as to what overloaded operators are being used.
snippet
Operand A( 2.3f );
Operand B( 4.5f );
Operand C( 0 );
Operand D( 0 );
C = A + B; // inline friend Operand Operand::operator+( const Operand& A, const Operand B );
D = C + 2.5f; // Overloaded Operator Defined as Class Member
D = 3.4f + B; // inline friend Operand Operand::operator+( const Value& value, const Operand A );
I've done this with the 4 common arithmetic operations +-*/
where one operator is defined in the class as a member and 2 friend operators are defined for each of the 4 operations. Here is the class definition.
Operand.h
#ifndef OPERAND_H
#define OPERAND_H
class Operand {
public:
static const float ZERO;
protected:
float operand_;
public:
explicit Operand( float a = float() ) : operand_( a ) {}
inline float getOperand() const {
return operand_;
}
inline Operand& operator+=( const float& value ) {
operand_ += value;
return *this;
}
inline Operand& operator-=( const float& value ) {
operand_ -= value;
return *this;
}
inline Operand& operator*=( const float& value ) {
operand_ *= value;
return *this;
}
inline Operand& operator/=( const float& value ) {
if ( isZero( value ) ) {
operand_ = 0;
} else {
operand_ /= value;
}
}
inline Operand operator+() const { // Unary
return *this;
}
inline Operand operator+( const float& value ) const {
return Operand( operand_ + value );
}
inline Operand operator-() const { // Unary
return Operand( -operand_ );
}
inline Operand operator-( const float& value ) const {
return Operand( operand_ - value );
}
inline Operand operator*( const float& value ) const {
return Operand( operand_ * value );
}
inline Operand operator/( const float& value ) const {
if ( isZero( value ) ) {
return Operand( 0 );
} else {
return Operand( operand_ / value );
}
}
inline friend Operand Operand::operator+( const Operand& A, const Operand B ) {
return Operand( A.getOperand() + B.getOperand() );
}
inline friend Operand Operand::operator+( const float& value, Operand A ) {
return Operand( value + A.getOperand() );
}
inline friend Operand Operand::operator-( const Operand& A, const Operand B ) {
return Operand( A.getOperand() - B.getOperand() );
}
inline friend Operand Operand::operator-( const float& value, Operand A ) {
return Operand( value - A.getOperand() );
}
inline friend Operand Operand::operator*( const Operand& A, const Operand B ) {
return Operand( A.getOperand() * B.getOperand() );
}
inline friend Operand Operand::operator*( const float& value, const Operand A ) {
return Operand( value * A.getOperand() );
}
inline friend Operand Operand::operator/( const Operand& A, const Operand B ) {
if ( isZero( B.getOperand() ) ) {
return Operand( 0 );
} else {
return Operand( A.getOperand() / B.getOperand() );
}
}
inline friend Operand Operand::operator/( const float& value, const Operand A ) {
if ( isZero( A.getOperand() ) ) {
return Operand( 0 );
} else {
return Operand( value / A.getOperand() );
}
}
inline static bool isZero( float value ) {
if ( (value > -ZERO) && (value < ZERO) ) {
return true;
}
return false;
} // isZero
}; // Operand
// #include "Operand.inl"
#endif // OPERAND_H
Operand.cpp
#include "Operand.h"
const float Operand::ZERO = static_cast<float>(1e-7);
What I want to do is to template this class.
So here is the same class only defined as a class template:
OperandT.h
#ifndef OPERAND_T_H
#define OPERAND_T_H
template <typename T>
class OperandT {
public:
static const T ZERO;
protected:
T operand_;
public:
explicit OperandT<T>( T a = T() ) : operand_( a ) {}
inline T getOperand() const {
return operand_;
} // getOperand
inline OperandT<T>& operator+=( const T& value ) {
operand_ += value;
return *this;
} // operator+=
inline OperandT<T>& operator-=( const T& value ) {
operand_ -= value;
return *this;
} // operator-=
inline OperandT<T>& operator*=( const T& value ) {
operand_ *= value;
return *this;
} // operator*=
inline OperandT<T>& operator/=( const T& value ) {
if ( isZero( value ) ) {
operand_ = 0;
} else {
operand_ /= value;
}
} // operator/=
inline OperandT<T> operator+() const {
return *this;
} // operator+ Unary
inline OperandT<T> operator+( const T& value ) const {
return OperandT<T>( operand_ + value );
} // operator+ Binary
inline OperandT<T> operator-() const {
return OperandT<T>( -operand_ );
} // operator- Unary (Negate Value)
inline OperandT<T> operator-( const T& value ) const {
return OperandT<T>( operand_ - value );
} // opeator- Binary (Subtraction)
inline OperandT<T> operator*( const T& value ) const {
return OperandT<T>( operand_ * value );
} // operator* Post Multiply
inline OperandT<T> operator/( const T& value ) const {
if ( isZero( value ) ) {
return OperandT<T>( 0 );
} else {
return OperandT<T>( operand_ / value );
}
} // operator/ Post Divide
/*/ Having Trouble With Operators When Using Templates and Friends
inline friend OperandT<T> OperandT<T>::operator+( const OperandT<T>& A, const OperandT<T> B ) {
return OperandT<T>( A.getOperand() + B.getOperand() );
}
inline friend OperandT<T> OperandT<T>::operator+( const float& value, OperandT<T> A ) {
return OperandT<T>( value + A.getOperand() );
}
inline friend OperandT<T> OperandT<T>::operator-( const OperandT<T>& A, const OperandT<T> B ) {
return OperandT<T>( A.getOperand() - B.getOperand() );
}
inline friend OperandT<T> OperandT<T>::operator-( const float& value, OperandT<T> A ) {
return OperandT<T>( value - A.getOperand() );
}
inline friend OperandT<T> OperandT<T>::operator*( const OperandT<T>& A, const OperandT<T> B ) {
return OperandT<T>( A.getOperand() * B.getOperand() );
}
inline friend OperandT<T> OperandT<T>::operator*( const float& value, const OperandT<T> A ) {
return OperandT<T>( value * A.getOperand() );
}
inline friend OperandT<T> OperandT<T>::operator/( const OperandT<T>& A, const OperandT<T> B ) {
if ( isZero( B.getOperand() ) ) {
return OperandT<T>( 0 );
} else {
return OperandT<T>( A.getOperand() / B.getOperand() );
}
}
inline friend OperandT<T> OperandT<T>::operator/( const float& value, const OperandT<T> A ) {
if ( isZero( A.getOperand() ) ) {
return OperandT<T>( 0 );
} else {
return OperandT<T>( value / A.getOperand() );
}
} */
inline static bool isZero( T value ) {
if ( (value > -ZERO) && (value < ZERO) ) {
return true;
}
return false;
} // isZero
}; // OperandT
#include "OperandT.inl"
#endif // OPERAND_T_H
OperandT.cpp
#include "OperandT.h"
template <typename T>
const T OperandT<T>::ZERO = static_cast<T>( static_cast<float>(1e-7) );
With the friend operators commented out it compiles and the in class defined operators work but when I uncomment the lower section of the class I end up getting compiler errors.
The few questions that I have are:
- Is there a valid way to defined friend operators or functions with class templates?
- If so, what is the appropriate syntax due to the strangeness of templates?
- If not, what are possible alternatives to achieve the same functionality to keep this as generic as possible?
- Last but not least, why is it that the friends work perfectly fine in the first class, but once you add the concept of templates, you end up having a train wreck?