Rafael Baptista
Rafael Baptista

Reputation: 11499

How to overload operators with a built-in return type?

Say I have a class, that wraps some mathematic operation. Lets use a toy example

class Test
{
public:
   Test( float f ) : mFloat( f ), mIsInt( false ) {}

   float mFloat;
   int   mInt;
   bool  mIsFloat;
};

I'd like to create an operator overload with the following prototype:

float operator=( const Test& test ) 
{ 
    if ( !test.mIsFloat ) return *this; // in this case don't actually do the assignment
    return test.mFloat;                      // in this case do it.
} 

So my questions are: can I overload operator= with a built-in return type? and if so, is there a way to refer to the built-in type?

I know I could do this if I wrapped the built-ins with a class. But in this case I want to have the assignment operator work with built-in types on the LHS

Example of usage:

Test t( 0.5f );
float f = t; // f == 0.5
int   i = 0;
i = t;       // i stays 0.

UPDATE: Thanks so much for the help. Expanding a little bit from the toy example so people understand what I'm really trying to do.

I have a configuration system that allows me to get config parameters from a tree of parameters with different type ( they can be integers, floats, strings, arrays etc. ).

I can get items from the tree with operations like this:

float updateTime = config["system.updateTime"];

But it is possible that "system.updateTime" does not exist. Or is of the wrong type. Generally for configuration I have a block of defaults, and then code to overide the defaults from the config:

float updateTime = 10;
const char* logFile = "tmp.log";
... etc etc...

I want to do something like:

updateTime = config["system.updateTime"];

Where the operation succeeds if there is an override. So generally the assignment doesn't happen if the return from operator[] is an "invalid" node in the tree.

Right now I solve it with a function like:

getConfig( config, "system.updateTime", updateTime );

But I would prefer to use assignment operator.

I could do this if I were willing to create classes to wrap the builtins.

class MyFloat
{
   operator=( const Test& test ) { if (test.isValidNode() ) f = test.float(); return *this; }
   float f;
}

But obviously it would be prefereable not to wrap built-ins with trivial classes just to overload assignment. Question is - is this possible in c++?

Upvotes: 1

Views: 794

Answers (4)

PiotrNycz
PiotrNycz

Reputation: 24347

You can't "overload/add" operators for basic types, but you can for your type Type. But this shall not be operator = but operator >> - like in istreams.

class Test
{
public:
   float mFloat;
   int   mInt;
   bool  mIsFloat;
   Test& operator >> (float& v) { if (mIsFloat) v = mFloat; return *this; }
   Test& operator >> (int& v) { if (!mIsFloat) v = mInt; return *this; }
};

Then you can:

int main() {
  float v = 2;
  Test t = { 1.0, 2, false };
  t >> v; // no effect
  t.mIsFloat = true;
  t >> v; // now v is changed
}   

Update

I want to do something like:

updateTime = config["system.updateTime"];

Then with my proposal, you cam:

config["system.updateTime"] >> updateTime;

Upvotes: 0

andre
andre

Reputation: 7249

Using template specialization:

class Config {
    template<typename T>
    void setValue(const std::string& index, T& value); //sets the value if available
};

template<float>
void Config::setValue(const std::string& index, float& value){...} //only sets float values

template<int>
void Config::setValue(const std::string& index, int& value){...} //only sets int values;

Upvotes: 1

John Dibling
John Dibling

Reputation: 101456

You already have one implicit conversion from float to Test, in the form of the convert constructor

class Test
{
public:
 /* ... */
Test( float f ) : mFloat( f ) /*...*/ {}       

};

Which will support conversions such as:

Test t(0.5f);

You will likely also want a copy-assignement operator in order to make further implicit conversions from float to Test possible:

class Test
{
public:

    Test& operator=(float f) { mFloat = f; return *this; }  
};

t = 0.75; // This is possible now

In order to support implicit conversion from Test to float you don't use operator=, but a custom cast operator, declared & implemented thusly:

class Test
{
public:
  /* ... */
  operator float () const { return mFloat; }
};

This makes implicit conversions posslible, such as:

float f = t;

As an aside, you have another implicit conversion happening here you may not even be aware of. In this code:

Test t( 0.5 );

The literal value 0.5 isn't a float, but a double. In order to call the convert constructor this value must be converted to float, which may result in loss of precision. In order to specify a float literal, use the f suffix:

Test t( 0.5f );

Upvotes: 1

cdhowie
cdhowie

Reputation: 168988

Based on your example, what you really want is an implicit conversion operator:

class Test
{
    // ...
public:
    operator float() const;
};

inline Test::operator float() const
{
    return mIsFloat ? mFloat : mInt;
}

If you want to conditionally do the assignment, then you need to take another approach. A named method would probably be the best option, all things considered... something like this:

class Test
{
public:
    bool try_assign(float & f) const;
};

inline bool Test::try_assign(float & f) const
{
    if (mIsFloat) {
        f = mFloat;
    }

    return mIsFloat;
}

Whatever you do, be careful that the syntactic sugar you introduce doesn't result in unreadable code.

Upvotes: 3

Related Questions