1

Say I had a structure that represented a special numerical value, like a float with added functionality. This is just an example.

struct MyCustomFloat {

    MyCustomFloat(float value);
    MyCustomFloat(double value);

    ...
};

Then I had other functions that used instances of that struct. Another example.

MyCustomFloat add(MyCustomFloat x, MyCustomFloat y);

Is it possible to implement a way where I can give a int/float/double/etc. as my arguments for functions such as these and have them automatically converted to the custom type?

This way the messy code:

MyCustomFloat result = add(MyCustomFloat(1.5), MyCustomFloat(3.14));

Could be replaced with a cleaner:

MyCustomFloat result = add(1.5, 3.14);

And it would keep developers from having to write multiple versions of functions accepting each type of valid constructor input.

MyCustomFloat add(int x, int y);
MyCustomFloat add(float x, float y);
MyCustomFloat add(double x, double y);
MyCustomFloat add(int x, double y);
...
Jared
  • 4,240
  • 4
  • 22
  • 27
  • You can use conversion operator, check http://stackoverflow.com/questions/1307876/how-do-conversion-operators-work-in-c and http://stackoverflow.com/questions/1383606/conversion-operators-in-c. – karastojko Nov 19 '15 at 06:43

2 Answers2

3

Could be replaced with:

It can, and you already halfway implemented it, because the constructors you already have will be used for implicit conversion.

#include <iostream>

class A {
public:    
    A(float f) : val_(f) {}
    A(double d) : val_(d) {}

    A& operator+= (const A& r) {
        val_ += r.val_; 
        return *this;
    }

    double val() const  { return val_; }

private:    
    double val_;    
};

A add(const A& l, const A& r) {
    A a = l;
    a += r;
    return a;
}    

int main()
{
     std::cout << add(3.14f, 2.71).val() << std::endl;    
}

live at Coliru's

If that is however "cleaner" is a different topic; if that wasn't my own code and would just see main(), I would expect that an "add" adds a POD float and a POD double, and be surprised that the expected result (POD) apparently has a fancy val member function all-of-a-sudden.

decltype_auto
  • 1,706
  • 10
  • 19
0

There several solutions to your problem:

  • overloaded functions: give en implementation for each reasonable type and combinations of types. They should just convert the parameters and call the method with the MyCustomFloat parameters. In that case you do not need the overloaded Constructors.

  • operator: implement const MyCustomFloat MyCustomFloat::operator+(const MyCustomFloat &a). This way you can always use a+b as an expression, no matter what types they are. In that case you can write a single add function taking MyCustomFloat as arguments and they are constructed from your overloaded constructors. No need for overloaded add function here.

  • templates: You will need a templated function for the general case, expecting the parameters to implement the + operator, i.e. being numerical types. Furthermore, you will need a template specialisation for MyCustomFloat. See example below. To mix types, you would need two templated types and three specialisations.

Working example:

#include <cstdio>

struct MyCustomFloat {

  double m_value;

public:

  MyCustomFloat(double a) : m_value(a){}
  double get() const {return m_value;}

  // uncomment this for use with operator+                                                                                   
  //const MyCustomFloat operator+(const MyCustomFloat &a) const {return m_value+a.get();}                                    
};

// use this whith operator+                                                                                                  
/*                                                                                                                           
MyCustomFloat add (const MyCustomFloat& a, const MyCustomFloat& b) {                                                         
  return MyCustomFloat(a+b);                                                                                                 
}                                                                                                                            
/**/

// uncomment this for use with template                                                                                      
template <typename T>
MyCustomFloat add(const T& a, const T& b) {
  return MyCustomFloat(a+b);
}

template <>
MyCustomFloat add<MyCustomFloat> (const MyCustomFloat& a, const MyCustomFloat& b) {
  return MyCustomFloat(a.get()+b.get());
}
/**/

int main (void){
  printf("%f\n",add(1.2,3.4).get());
  printf("%f\n",add(MyCustomFloat(1.2),MyCustomFloat(3.4)).get());
  return 0;
}
steffen
  • 8,572
  • 11
  • 52
  • 90