C++ function that can take integer or float or double or any other castable to float

Viewed 139

I am trying to implement a Vector3 struct in C++. I am overloading the "*" operator for handling multiplication with the scalar values. So it will work like this:

v1 = Vector3(1.0f, 1.0f, 1.0f);
v2 = 2*v1;
v3 = 2.4f*v1;
v4 = 2.4*v1;

All 3 operations will return a Vector3 instance. However, I don't want to implement 3 functions for this purpose.

    Vector3 operator * (int& const val) {
       float _val = static_cast<float> (val);
       return Vector3(x * _val, y * _val, z * _val);
    }

   Vector3 operator * (double& const val) {
           float _val = static_cast<float> (val);
           return Vector3(x * _val, y * _val, z * _val);
   }
   
   Vector3 operator * (float& const val) {
               return Vector3(x * val, y * val, z * val);
       }
    

Is there a better way of doing this with one function?

3 Answers

Since you are casting all of the types to float again, you don't need that.

If you defined your function to accept a float, then passed an int or any convertible type, it would be cast to float automatically. The following code shows that

#include <typeinfo>
#include <iostream>

struct Vector3
{
    Vector3(float x, float y, float z): x{x}, y{y}, z{z}{}
    float x, y, z;
    Vector3 operator*(float val)const{
        return Vector3{val * x,val * y,val * z};
    }
};

int main(){
    Vector3 v1{1,2,3};
    auto v2 = v1*2;
    std::cout << typeid(v2.x).name();
}

Live

If you want to use the multiplication in reverse order

#include <typeinfo>
#include <iostream>

struct Vector3
{
    Vector3(float x, float y, float z): x{x}, y{y}, z{z}{}
    float x, y, z;
};
Vector3 operator*(float val, const Vector3& v){
    return Vector3{val * v.x,val * v.y,val * v.z};
}

int main(){
    Vector3 v1{1,2,3};
    auto v2 = 2*v1;
    std::cout << typeid(v2.x).name();
}

I used public members for simplicity. u may want to use private ones with setters and getters.

If you really must use reference parameters and the float data type internally, and you wish to avoid compiler warnings about implicit conversions, then you can use a templated operator function (note also the modified position of the const qualifier):

    template<typename T>
    Vector3 operator * (const T& val)
    {
        float mul = static_cast<float>(val); // Change this to any specific conversion/cast you want
        return Vector3(x * mul, y * mul, z * mul);
    }

You will also need to use a Vector3 object as the first operand of the * operator:

int main()
{
    Vector3 v1 = Vector3(1.0f, 1.0f, 1.0f);
//  Vector3 v2 = 2 * v1;
//  Vector3 v3 = 2.4f * v1; // These won't work!
//  Vector3 v4 = 2.4 * v1;
    Vector3 v2 = v1 * 2;
    Vector3 v3 = v1 * 2.4f; // But these will
    Vector3 v4 = v1 * 2.4;
    return 0;
}

EDIT: If you want a 'commutative' operator (that is, one in which you could use the scalar operand in either position), then you can declare a friend operator that takes two arguments (the constant and a class reference):

    template<typename T>
    friend Vector3 operator * (const T& val, const Vector3& vec)
    {
        float mul = static_cast<float>(val); // Change this to any specific conversion/cast you want
        return Vector3(vec.x * mul, vec.y * mul, vec.z * mul);
    }

As I see, it's enough to define/declare variant with double and it will work with floats and integers as well.

Here is compilable example (just test & demonstration):

class Vector3 {
public:
    Vector3(double x, double y, double z): x(x), y(y), z(z) { }

    Vector3 operator * (double val) {
        return Vector3(x * val,
                       y * val,
                       z * val);
    }

private:
    double x { 0 };
    double y { 0 };
    double z { 0 };
};


int main()
{
    int a = 1;
    float b = 2.1;
    double c = 3.5;

    Vector3 vec1(1, 2.1f, 3);
    Vector3 vec2(a, b, c);

    auto vec3 = vec1 * a;
    auto vec4 = vec1 * b;
    auto vec5 = vec1 * c;

    return 0;
}
Related