-1

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?
Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • What does this `inline friend Operand Operand::operator+( const Operand& A, const Operand B )` mean? – Danh Nov 10 '16 at 06:39
  • @Danh Adding to Operand Objects together look at the snippet part of the code towards the top. Since this is a class object, the class defined operator does class instance + value, where the two friends do value + class instance and the one you are asking about does class instance + another class instance. – Francis Cugler Nov 10 '16 at 12:29
  • Have you tested it? I don't think it will work – Danh Nov 10 '16 at 12:35
  • @Danh Yes I have tested it and it does work at least in Visual Studio 2012,13 & 15 and if you don't defined the two friend overloaded operators then you can only do this: `Operand a(2.5f); a + 1.2f;` Without the other two versions defined you can not do these: `Operand a(2.5f), b(3.5f), c(0); c = a + b; /*or*/ c = 9.2f + b;` – Francis Cugler Nov 10 '16 at 12:40
  • Why the down vote? – Francis Cugler Nov 10 '16 at 12:45
  • Because it doesn't work, see [this](http://melpon.org/wandbox/permlink/TLTTypaZ7oXhb5Rq) – Danh Nov 10 '16 at 13:00
  • Take a book from [this list](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) then learn how to use C++ – Danh Nov 10 '16 at 13:01
  • @Danh I'm not using GCC or Clang; I'm using Visual Studio and It Does Work Check Here! http://rextester.com/DUT65625 – Francis Cugler Nov 11 '16 at 03:10
  • 1
    The proper way is `friend Operand operator+(Operand A, Operand B)` or `friend Operand operator+(const Operand& A, const Operand& B)`, I'm going to file a bug to MSFT – Danh Nov 11 '16 at 03:54
  • Bug reported with detail explanation why this should fail: https://connect.microsoft.com/VisualStudio/feedback/details/3111286 – Danh Nov 11 '16 at 04:14
  • @Danh I can take that into consideration but it also compiles and runs without error in my actual VS2013, 15 compilers. – Francis Cugler Nov 11 '16 at 07:47
  • 1
    That means it's the bug of compiler – Danh Nov 11 '16 at 08:19

1 Answers1

0

After having a brief conversation with Danh in the comment section He has brought to my attention that the way I was able to implement the overloaded friend operators in visual studio 2013 - 15 should be considered a bug. Yes it will compile and run without error on visual studio as well as this website for visual studio compilers rextesters.com. He has stated that the proper way to implement overloaded friend operators is as such:

The proper way is friend Operand operator+(Operand A, Operand B) or friend Operand operator+(const Operand& A, const Operand& B), I'm going to file a bug to MSFT – Danh

He also showed me where it failed to compile under GCC or Clang from here: http://melpon.org

I told him I would take this into consideration and I did. So I updated my Operand header file accordingly and it compiles, builds and runs correctly. Then I went ahead and fixed the template version and it is working as well.

Here are the updated class headers.

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 operator+( const Operand& A, const Operand& B ) {
        return Operand( A.getOperand() + B.getOperand() );
    }
    inline friend Operand operator+( const float& value, const Operand& A ) {
        return Operand( value + A.getOperand() );
    }

    inline friend Operand operator-( const Operand& A, const Operand& B ) {
        return Operand( A.getOperand() - B.getOperand() );
    }
    inline friend Operand operator-( const float& value, const Operand& A ) {
        return Operand( value - A.getOperand() );
    }

    inline friend Operand operator*( const Operand& A, const Operand& B ) {
        return Operand( A.getOperand() * B.getOperand() );
    }
    inline friend Operand operator*( const float& value, const Operand& A ) {
        return Operand( value * A.getOperand() );
    }

    inline friend 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 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;
    }    
}; // Operand

//#include "Operand.inl"

#endif // OPERAND_H

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_; 
    } 

    inline OperandT<T>& operator+=( const T& value ) {
        operand_ += value;
        return *this;
    } 

    inline OperandT<T>& operator-=( const T& value ) {
        operand_ -= value;
        return *this;
    } 

    inline OperandT<T>& operator*=( const T& value ) {
        operand_ *= value;
        return *this;
    } 

    inline OperandT<T>& operator/=( const T& value ) {
        if ( isZero( value ) ) {
            operand_ = 0;
        } else {
            operand_ /= value;
        }
    } 

    inline OperandT<T> operator+() const { 
        return *this; 
    } 

    inline OperandT<T> operator+( const T& value ) const { 
        return OperandT<T>( operand_ + value ); 
    } 

    inline OperandT<T> operator-() const {
        return OperandT<T>( -operand_ ); 
    } 

    inline OperandT<T> operator-( const T& value ) const {
        return OperandT<T>( operand_ - value );
    }

    inline OperandT<T> operator*( const T& value ) const {
        return OperandT<T>( operand_ * value );
    }

    inline OperandT<T> operator/( const T& value ) const {
        if ( isZero( value ) ) {
            return OperandT<T>( 0 );
        } else {
            return OperandT<T>( operand_ / value );
        }
    }

    inline friend OperandT<T> operator+( const OperandT<T>& A, const OperandT<T>& B ) {
        return OperandT<T>( A.getOperand() + B.getOperand() );
    }
    inline friend OperandT<T> operator+( const float& value, const OperandT<T>& A ) {
        return OperandT<T>( value + A.getOperand() );
    }

    inline friend OperandT<T> operator-( const OperandT<T>& A, const OperandT<T>& B ) {
        return OperandT<T>( A.getOperand() - B.getOperand() );
    }
    inline friend OperandT<T> operator-( const float& value, const OperandT<T>& A ) {
        return OperandT<T>( value - A.getOperand() );
    }

    inline friend OperandT<T> operator*( const OperandT<T>& A, const OperandT<T>& B ) {
        return OperandT<T>( A.getOperand() * B.getOperand() );
    }
    inline friend OperandT<T> operator*( const float& value, const OperandT<T>& A ) {
        return OperandT<T>( value * A.getOperand() );
    }

    inline friend 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> 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;
    }
}; // OperandT

//#include "OperandT.inl"

#endif // OPERAND_T_H

Now my code is working properly using templates all thanks to Danh!

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59