Adam NA
Adam NA

Reputation: 95

How to overload an operator for integer, float and double data types simultaneously in C++

I'm creating a 2D coordinate class (named “Point”) to help me learn C++. I want to be able to perform basic arithmetic operations (+, -, *, / ...) on Point class objects (e.g. Point_a + Point_b). However, I also want to be able to perform such operations between Points and other variable types (int/float/double).

This can be done using operator/function overloading. It can be seen from my code below (addition only) that I must, as far as I am aware, include two additional functions for each additional variable type, one for the “Point + int/float/double” form and one for the “int/float/double + Point” form.

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

There seems to be a lot of repetition, particular between “Point + int/float/double” type functions. I was wondering if there is a way that this could be shorted a little. Say, rather than having individual versions for integer, float and double I could have one version which would handle all three. For example converting code blocks "A" and "B" into something like:

    ...

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

...

The above code section is intended to be representational of the desired result rather than actually functional (e.g. int OR float OR double).

In short, is there any way that I can make "friend Point operator+(const Point &p1, int val);" and its corresponding function (in code block B) accept integer, float and double values, or do I need to have an individual one for each variable type?

Thank you for your time.

Upvotes: 6

Views: 9708

Answers (5)

Alan
Alan

Reputation: 46873

Separate solution than the template solution is to define constructors for the types you want to support, and have a single operator+ that works on Point types.

The compiler will implicitly call the proper constructor to convert the built-in type, before invoking operator+.

This simplifies the implementation, at the expense of wasted temp objects.

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

There is a stylistic argument about explicitly white-listing supported types via the constructors, and blacklisting via static_asserts.

Upvotes: 3

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275740

Point's operator+ implementations all converts its non-Point arguments to doubles before doing a + on elements.

The float and int overloads are nearly pointless. Just write the two double overloads.

Built-in conversion from int and float will then occur in the operator+ call.


If you really need int/float overloads and you know why and have tested that the double overload doesn't work, consider fixing the broken template conversion operator which is about the only way I can see this bbeing the case.

Failing that, cast to double explicitly.

Failing that, maybe write a template + that takes any T convertible to double, then converts and calls the above + for double. Then add a paragraph of documentation why you had to do something this stupid and complex instead of just overloading + with double. Then read that paragraph and change your mind and stick with + overloaded with double.

Upvotes: 1

Richard Hodges
Richard Hodges

Reputation: 69902

Other answers mention templates but in actual fact, all numeric types will automatically be promoted to doubles.

Therefore, you only need to provide operators in terms of Point and double.

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

Upvotes: 7

user0042
user0042

Reputation: 8018

Such can be more generalized by using templates and specializing the exceptional case, instead of providing an overloaded version for each and every matching type:

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

Upvotes: 5

Justin
Justin

Reputation: 25337

You can absolutely do this using templates:

// 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

This uses std::enable_if and std::is_arithmetic, to make this function only accept types that are "arithmetic types", basically integers and floats.

Upvotes: 2

Related Questions