0

What's the difference between non-member function and member function when type conversions should apply to all parameters?

That from the book, "Effective C++":

First, make operator* a member function:

class Rantional {

public:
    Rantional(int numerator=0,int denominator=0)
        : numerator(numerator), denominator(denominator){
        std::cout << "constrctor be called" << std::endl;
    }

    const Rantional operator*(const Rantional& rhs){
        return Rantional(numerator * rhs.numerator, denominator * rhs.denominator);   // member function
    }
private:
    int numerator;
    int denominator;
};

Rantional oneHalf(1,2);
Rantional result = 2 * oneHalf;   // error

Second, make operator* a non-member function:

 const Rantional operator*(const Rantional& lhs,const Rantional& rhs){
        return Rantional(lhs.numerator * rhs.numerator, lhs.denominator * rhs.denominator);   // non-member function

Rantional oneHalf(1,2);
Rantional result = 2 * oneHalf;   // ok

What's different above?

There is a explanation of it in the book, but I can't understand. So can you tell me with a simple description?

The implicit parameter corresponding to the object on which the member function is invoked — the one this points to — is never eligible for implicit conversions.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
jptang
  • 1
  • 2
  • 2
    I can't understand your question as it is; can you please include a clear example of what you mean? – BoBTFish Oct 19 '21 at 07:56
  • 1
    `2` is an `int` literal. It can *become* a rational (with the denominator 0, which is probably incorrect) because there is a constructor that can accept one `int` argument. However, that conversions is not considered before gathering all the `operator*`s to do overload resolution, so in the example the member `*` is ignored, because the lhs isn't a `Rational` already. Both would allow `oneHalf * 2` – Caleth Oct 19 '21 at 08:36
  • Side note: don't return const values from function, unless you're using C++98 (EC++ was written way before C++11, so this is understandable), as this will prevent move operations. You might want to [overload on reference qualifiers](https://stackoverflow.com/a/13099997/4885321) instead. – alagner Oct 19 '21 at 20:59

1 Answers1

0

Consider the following statement from both examples:

Rational result = 2 * oneHalf;

To make this compile using an operator declared as a method on the class, the integer literal '2' would need to be converted (implicitly) to a 'Rational' type. This conversion is not considered by the compiler. The member operator* is never added to the list of function candidates during overload resolution and as a result is never found. Note that the statement:

Rational result = oneHalf * 2;

Will work fine, no implicit conversion on the left hand side is necessary. Since 'oneHalf' is already a 'Rational' the member operator* will be added to the set of candidates during overload resolution and will be found and used.

The non-member version of the operator* function will be considered during resolution because it is in global or namespace scope and will form a match with a single implicit conversion on the 'int' literal.

A lot of detailed information about how overload resolution is performed can be found on: https://en.cppreference.com/w/cpp/language/overload_resolution

TimVdG
  • 2,063
  • 1
  • 11
  • 16