【问题标题】:How to overload an operator for integer, float and double data types simultaneously in C++如何在 C++ 中同时重载整数、浮点和双精度数据类型的运算符
【发布时间】:2018-03-23 16:16:25
【问题描述】:

我正在创建一个 2D 坐标类(名为“Point”)来帮助我学习 C++。我希望能够对 Point 类对象(例如 Point_a + Point_b)执行基本的算术运算(+、-、*、/ ...)。但是,我也希望能够在 Points 和其他变量类型(int/float/double)之间执行此类操作。

这可以使用运算符/函数重载来完成。从下面的代码中可以看出(仅添加),据我所知,我必须为每个附加变量类型包含两个附加函数,一个用于“Point + int/float/double”形式,一个用于“int/float/double + Point”形式。

#include <iostream>

using namespace std;

class Point
{
    private:
        double x, y;

    public:
        Point(double x_in, double y_in){
            setX(x_in);
            setY(y_in);
        }    
        // Getters and Setters omitted for brevity

        // --- Start of Addition Operator Overloads --- (Code Block A)

        friend Point operator+(const Point &p1, const Point &p2);   //Point + Point
        friend Point operator+(const Point &p1, int val);           //Point + Int
        friend Point operator+(int val, const Point &p1);           //Int + Point
        friend Point operator+(const Point &p1, float val);         //Point + Float
        friend Point operator+(float val, const Point &p1);         //Float + Point     
        friend Point operator+(const Point &p1, double val);        //Point + Double
        friend Point operator+(double val, const Point &p1);        //Double + Point

        // --- End of Addition Operator Overloads --- (Code Block A)
};

// --- Start of Addition Operator Overload Functions --- (Code Block B)

Point operator+(const Point &p1, const Point &p2){
    return Point(p1.x + p2.x, p1.y + p2.y);
}

Point operator+(const Point &p1, int val){
    return Point(p1.x + val, p1.y + val);
}

Point operator+(int val, const Point &p1){
    return Point(p1.x + val, p1.y + val);
}

Point operator+(const Point &p1, float val){
    return Point(p1.x + val, p1.y + val);
}

Point operator+(float val, const Point &p1){
    return Point(p1.x + val, p1.y + val);
}

Point operator+(const Point &p1, double val){
    return Point(p1.x + val, p1.y + val);
}

Point operator+(double val, const Point &p1){
    return Point(p1.x + val, p1.y + val);
}

    // --- End of Addition Operator Overload Functions --- (Code Block B)
int main()
{
    Point point_a( 2.00, 20.00);
    Point point_b( 0.50,  5.00);
    Point point_c = point_a + point_b;
    cout << "X = " << point_c.getX() << " and Y = " << point_c.getY() << endl;
    return 0;
}

似乎有很多重复,特别是在“Point + int/float/double”类型的函数之间。我想知道是否有办法可以将其缩短一点。比如说,我可以有一个版本来处理所有三个,而不是为整数、浮点和双精度提供单独的版本。例如将代码块“A”和“B”转换成类似的东西:

    ...

    // --- Start of Addition Operator Overloads --- (Code Block A)

    friend Point operator+(const Point &p1, const Point &p2);           //Point + Point
    friend Point operator+(const Point &p1, int||float||double val);    //Point + Int/Float/Double
    friend Point operator+(int||float||double val, const Point &p1);    //Int/Float/Double + Point

    // --- End of Addition Operator Overloads --- (Code Block A)

    ...

...

// --- Start of Addition Operator Overload Functions --- (Code Block B)

Point operator+(const Point &p1, const Point &p2){
    return Point(p1.x + p2.x, p1.y + p2.y);
}

Point operator+(const Point &p1, int||float||double val){
    return Point(p1.x + val, p1.y + val);
}

Point operator+(int||float||double val, const Point &p1){
    return Point(p1.x + val, p1.y + val);
}

// --- End of Addition Operator Overload Functions --- (Code Block B)

...

上述代码部分旨在表示所需结果,而不是实际功能(例如 int OR float OR double)。

简而言之,有什么方法可以让“friend Point operator+(const Point &amp;p1, int val);”及其对应的函数(在代码块 B 中)接受整数、浮点数和双精度值,或者我需要为每个变量类型设置一个单独的值吗? ?

感谢您的宝贵时间。

【问题讨论】:

  • 可能是一个模板?
  • 顺便说一句:Point(double x_in, double y_in){ setX(x_in);设置Y(y_in); } ' 是多余的。您可以直接在构造函数中设置 x 和 y。

标签: c++ class math operator-overloading


【解决方案1】:

这可以通过使用模板和专门化例外情况来更普遍化,而不是为每个匹配类型提供重载版本:

class Point
{
    public:
        // ...        
        template<typename T>
        friend Point operator+(const Point &p1, T p2);             
};

template<typename T>
Point operator+(const Point &p1, T val){
    static_assert(std::is_artithmetic<T>::value,"An arithmetic type is required");
    return Point(p1.x + val, p1.y + val);
}

// Specialization for Point
template<> 
Point operator+(const Point &p1, const Point& val){
    return Point(p1.x + val, p1.y + val);
}

【讨论】:

    【解决方案2】:

    其他答案提到了模板,但实际上,所有数字类型都会自动提升为双精度类型。

    因此,您只需提供Pointdouble 的运算符即可。

    #include <iostream>
    
    using namespace std;
    
    class Point
    {
        private:
            double x, y;
    
        public:
            Point(double x_in, double y_in){
                setX(x_in);
                setY(y_in);
            }    
            // Getters and Setters omitted for brevity
    
            double getX() const { return x; }
            double getY() const { return y; }
    
            void setX(double v) { x = v; }
            void setY(double v) { y = v; }
    
            // unary ops
            Point& operator+=(Point const& other)
            {
                x += other.x;
                y += other.y;
                return *this;
            }
    
            Point& operator+=(double v)
            {
                x += v;
                y += v;
                return *this;
            }
    
            // --- Start of Addition Operator Overloads --- (Code Block A)
    
            friend Point operator+(Point p1, const Point &p2)
            {
                p1 += p2;
                return p1;
            }
    
            friend Point operator+(Point p1, double val)
            {
                p1 += val;
                return p1;
            }
    
            friend Point operator+(double val, Point p1)
            {
                p1 += val;
                return p1;
            }
            // --- End of Addition Operator Overloads --- (Code Block A)
    };
    
    
        // --- End of Addition Operator Overload Functions --- (Code Block B)
    int main()
    {
        Point point_a( 2.00, 20.00);
        Point point_b( 0.50,  5.00);
        Point point_c = point_a + point_b;
        point_c += 10;
        Point point_d = point_c + 10;
        Point point_e = point_d + 10.1;
        cout << "X = " << point_c.getX() << " and Y = " << point_c.getY() << endl;
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      与模板解决方案不同的解决方案是为您想要支持的类型定义构造函数,并拥有一个适用于 Point 类型的 operator+。

      在调用 operator+ 之前,编译器会隐式调用适当的构造函数来转换内置类型。

      这以浪费临时对象为代价简化了实现。

      // Example program
      #include <iostream>
      #include <string>
      
      class Point {
        private:
          double x, y;
        public:
          Point(int val) {
              this->x = val;
              this->y = val;
          }
      
          Point(double x, double y) {
              this->x = x;
              this->y = y;
          }
      
          friend Point operator+(const Point &p1, const Point &p2) {
              return Point(p1.x + p2.x, p1.y + p2.y);   
          }
      };
      
      int main()
      {
        Point p(1,2);
        Point q = p + 10;
        Point v = 10 + p;
      }
      

      关于通过构造函数将支持的类型显式列入白名单,以及通过 static_asserts 将支持的类型列入黑名单,存在一个风格论点。

      【讨论】:

        【解决方案4】:

        您完全可以使用模板来做到这一点:

        // inside the class
        template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
        friend Point operator+(const Point &p1, T val) {
            // function definition
        }
        
        // Don't forget to define the function for T on the left hand side
        

        这使用了std::enable_ifstd::is_arithmetic,使这个函数只接受“算术类型”的类型,基本上是整数和浮点数。

        【讨论】:

        • 警告:如果 T 是一个参考,这将无法按预期工作。
        • @user4581301 我不希望T 成为调用此函数的参考,因为我将val 作为值。
        • 为什么要采用这种方法,而不是定义接受 int、float 和 double 的构造函数,然后只使用一个接受两个点的友元函数?然后编译器会选择合适的转换构造函数。
        • @Alan 这是个好主意。有时,您有一些设计要求阻止您这样做,但这绝对是解决问题的好方法。您可以将其作为单独的答案发布
        【解决方案5】:

        Point 的 operator+ 实现在对元素执行 + 之前都将其非 Point 参数转换为双精度值。

        floatint 重载几乎毫无意义。只需编写两个double 重载即可。

        intfloat 的内置转换将在 operator+ 调用中进行。


        如果您确实需要 int/float 重载并且您知道原因并且已经测试过双重重载不起作用,请考虑修复损坏的模板转换运算符,这是我看到这种情况的唯一方法。

        如果做不到,则显式转换为 double。

        做不到这一点,也许编写一个模板+,将任何T 转换为double,然后转换并调用上述+double。然后添加一段文档,为什么你必须做这么愚蠢和复杂的事情,而不是仅仅用double 重载+。然后阅读该段并改变主意并坚持使用+ 重载double

        【讨论】:

          猜你喜欢
          • 2018-03-11
          • 2015-02-20
          • 2011-10-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多