使用 Friend 运算符在模板 类 中重载运算符

Overloading Operators in Templates Classes with Friend Operators

考虑下面的 class Operand:这是一个非常简单的 class 因为它接受一个参数并定义一堆重载运算符并且为了简单测试它class 我选择使用 float 类型,因为有除法运算符。如果您注意到某些运算符,它们被定义为朋友,因此可以在它们的代码中执行此操作:

编辑 有人在评论里问什么

inline friend Operand Operand::operator+( const Operand& A, const Operand B ) 

是什么意思?它是一个重载运算符,但由于已经为 class 本身定义了一个成员,因此它被声明为 class 的友元并在 class 中定义,但不是 class会员。这允许用户将两个 Operand class 实例或对象加在一起,并 returns 返回新计算的值作为 Operand 对象。我将在 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 );

我用 4 个常见算术运算 +-*/ 完成了此操作,其中一个运算符在 class 中定义为成员,并且为 4 个运算中的每一个定义了 2 个友元运算符。这是 class 定义。

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); 

我想做的是模板化这个class。

所以这里是相同的 class 仅定义为 class 模板:

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) );

如果友商运算符被注释掉,它可以编译,并且在 class 中定义的运算符可以工作,但是当我取消注释 class 的下半部分时,我最终会遇到编译器错误。

我的几个问题是:

在评论区与 Danh 进行了简短的交谈后,他引起了我的注意,我能够在 visual studio 2013 - 15 中实现 overloaded friend operators 的方式应该被认为是一种漏洞。是的,它将在 visual studio 上编译 运行 并且在 visual studio 编译器 rextesters.com 上没有错误。他指出实现重载友元运算符的正确方法是这样的:

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

他还从这里向我展示了在 GCC 或 Clang 下编译失败的地方:http://melpon.org

我告诉他我会考虑到这一点,我做到了。所以我相应地更新了我的 Operand header 文件,它正确地编译、构建和 运行s。 然后我继续修复模板版本,它也能正常工作。

这是更新后的 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

感谢 Danh

现在我的代码可以使用模板正常工作了