Search code examples
c++operator-overloadingunions

Typecast Overloading On Union Without Overloading +=


union word{
    uint16_t value;

    struct{
        uint8_t low;
        uint8_t high;
    };


    inline  word(uint16_t value) noexcept
        :value(value)
    {}


    inline word &operator=(uint16_t rhs) noexcept{
        value = rhs;
        return *this;
    }

    inline operator uint16_t() const noexcept{
        return value;
    }
}

I am trying to define a little endian 2 byte type where it is easy to access the low and high byte. In addition, I want the "word" type to act completely like a uint16_t when calling any arithmetic operator. Hence, I have overloaded the typecast operator for uint16_t.

But I have run into a slight problem:

word w1 = 5;
w1 = w1 + w1;  //this works fine due to implicit conversion
w1 += w1; //fails to compile.  lhs will not implicitly convert

I understand why it fails to compile. I'd like to have to avoid overloading all of the arithmetic operators such as +=, -=, &=, |=, etc. Is there anyway to avoid having to define all of the operators? It is likely I will need most of them.

Thank you very much!


Solution

  • The problem is with this line:

    inline operator uint16_t() const noexcept{
       return value;
    }
    

    When you do

    w1 += w1;
    

    the left hand w1 is implicitly converted to a uint16_t. However, you are actually returning a copy of w1.value which, as a result of the conversion, is a temporary object. And you are not allowed to assign to a temporary object. And if you magically can, the changes will not reflect to w1.value, something you do not want.

    To solve your problem, return a reference of value instead, and make the conversion function not const, as you would absolutely want value to be modifiable.

    inline operator uint16_t&() noexcept{
       return value;
    }
    

    Here is a live example showing that the solution works.


    However, I recommend you to read this question: Operator overloading

    Explicitly overloading the operators of your class would be much safer and predictable than relying on implicit conversions, which sometimes would yield weird results and compile errors.