Hi I have a question about operator overloading. Help!


#1

Recently, I read Thinking in C++;
In chapter 12 about operator overloading, I was confused about why the constant can directly be brought to a object (eg: b1 = 9; b2 = 47;)
please tell me :slight_smile:

In below is a whole codes.





05


#2

Others that know the internals of C++ better than me are welcome to answer.

I do not think that a constant is being assigned directly to the lvalue type Byte. Rather, I think the compiler converts the constant “9” to rvalue type Byte first, and then assigns that Byte object to lvalue b1.

@Pharap ?


#3

Indeed, there is an implicit constructor for Byte that takes as a single parameter an unsigned char:

public:
    Byte(unsigned char bb = 0) : b(bb) {}

Unless you declare such a constructor with explicit, it’ll be called directly when needing a Byte while using an integer literal.

Adding explicit would require you to do b1 = Byte(*); instead.


#4

Thanks everyone !!!
According to your explanation, I supposed the complier do something below about " b1 = 9 ":
1、look for “=” operator overloading, and find “Byte object = Byte object”
2、find rvalue is a constant, and try to look for how to convert to a Byte object in Byte class constructors
3、find a constructor using only argument of int, and try to Byte(9) to build a Byte object.
4、finially, using “=” operator overloading

BUT!!! This is only a supposition!!!:joy:


#5

@carbonacat’s answer is correct about why it’s possible.

If there’s a (non-explicit) constructor for a type T that accepts a single parameter of type U (or const U &) then using an assignment operator t = u; (where t is an object of type T and u is an expression of type U) will match that constructor.

However, it behaves slightly differently depending on the context…

For T t = u;, the code is translated to T t(u), i.e. the constructor is called directly.

For t = u; (where t has already been declared), the code is translated to t = T(u),
i.e. a temporary object of type T is created from u and then assigned to t.


Technicallly it would find:

  • Byte & Byte::operator=(const Byte &) - the ‘copy assignment operator’
  • Byte & Byte::operator(Byte &&) - the ‘move assignment operator’

Both of which are implicitly declared by the compiler and would be functionally equivalent.

Actually it would find:

  • Byte::Byte(unsigned char bb = 0) - a single parameter constructor (with a default argument)

It would then attempt to implicitly convert the integer literal (of type int) to an unsigned char,
which it can do because there’s an implicit conversion between the two.

So actually there’s two implicit conversions happening:

  • int -> unsigned char
  • unsignec char -> Byte

Usually the term ‘operator overloading’ is only applied if there are user-defined operator overloads.
In this case operator= only has implicitly-defined overloads,
so most people wouldn’t refer to it as ‘operator overloading’.


Lastly I’d like to say that the code in “Thinking in C++” does a few things I’m not happy about.

Things I'm not happy about...
  • using namespace std; is bad practice
  • Overloading operator&& and operator|| is bad practice
  • Returning int from an overloaded comparison operator (operator<, operator>, operator<=, operator>=, operator==, operator!=) is bad practice - they should return bool.
    • This can even have a negative performance impact.
  • Many overloaded operators return const Byte instead of just Byte, which is redundant and compilers will complain about this (i.e. they will provide a warning)
    • The reason people used to do this is to make code like (Byte(5) + Byte(6)) = Byte(7); fail to compile, but these days it’s considered bad practice
    • This is specfically about returning const T, returning const T & is something entirely different
  • Making it so that Byte() initialises the internal value to 0 can negatively affect performance, instead there should be two constructors:
    • Byte() = default;
    • Byte(unsigned char b) : b(b) {}
  • Using short non-descriptive variable names (like b, b1, b2, k) is generally a bad idea. Good code should have meaningful variable names
  • Chained assignment like b1 = b2 = b3; makes me frown. One assignment per statement please.
  • Using macros like TRY2 is generally bad, don’t do it in any serious code if you can avoid it.

#6

Thanks Pharap!!!
I’m agree with you about that code has a few issue.
but I think <Thinking in C++> better than other books in respect about how to understand easily in C++.