1. Clause 24 gives a rational example of why only the Non-member function has the ability to "implement implicit type conversions on all arguments". Rational is defined as follows:
class rational{ public int numerator =0 , 1 ); int numerator () const ; int denominator () const ; private : int numerator; int denominator;};
View Code
Operator* is declared as the Non-member function of rational:
Const operator* (const rational& lhs,const rational& RHS);
This is true when the template is not involved, and if you upgrade rational to a class template, it might look like this:
Template<typename t>classrational{ Public: Rational (Constt& numerator=0, denominator=1); ConstT numerator ()Const; ConstT Denominator ()Const; ...}; Template<typename t>ConstRational<t>operator*(Constrational<t>&LHS,Constrational<t>&RHS) { ...}
View Code
Then there may be the following code:
rational<int> Onehalf (1,2); Rational<int> result=onhalf*2;
It seems that "onhalf*2" should make the function template operator* and invoke it, but in fact the compilation does not pass, the root cause is that the compiler "never takes an implicit conversion into consideration when the template argument is pushed into the process": the conversion function is actually used during the function call ( If operator* is a function rather than a function template, but before invoking a function, you must know that the function exists, and in order to know it, you must first derive the parameter type for the relevant function template (before you can present the appropriate functions). However, the implicit type conversions that occur through constructors are not considered in the derivation process of the template argument.
2. It is possible to change this situation with the use of one fact: a friend declaration within a template class can refer to that particular function. That is to say rational can declare operator* as a friend function of it, class The template does not rely on the template argument derivation, so the compiler can always know T when class rational is present, so rational<t> class declares the appropriate operator* The friend function simplifies the whole problem:
Template<typename t>class rational{public: operator* ( Const rational& LHS, const rational& RHS); ...}; Template<typename t>constoperator* (const rational<t>& LHS, Const rational<t>& rhs) { ...}
View Code
Now the call to Operator* can be compiled, the change is: When Onehalf is declared, class rational<int> is manifested, and as part of the process, friend function operator* is automatically declared as a function rather than a function template, so the compiler can use an implicit conversion function when calling it.
This is not the end, although the above code is compiled, but cannot link--rational<int> operator* (const rational<int>&lhs,const Rational<int >&RHS) has been declared, but has not been defined. Using a template is not possible, because when rational is present, operator* is simply being instantiated as a normal function declaration, and the compiler will not think of it and operator* A function template is associated and a function entity is instantiated for it.
The solution is to merge the operator* function body into its declaration:
Template<typename t>class rational{public: operator* ( Const rational& LHS, const rational& RHS) { return Rational (Lhs.numerator () *rhs.numerator (), lhs.denominator () *rhs.denominator ()); ...};
View Code
At this point, the call to operator* compiles the link and executes.
3. In these terms, Friend's role is no longer to elevate access to a function or class, but rather to make a type conversion happen on all parameters: in order for the function to be automatically instantiated, it needs to be declared inside the class, The only way to declare the Non-member function inside a class is to make it a friend.
Since operator* needs to be defined within rational, it is declared as inline by default, in order to minimize the impact of this inline declaration (in this case, operator* is already a single-line function, but a more complex function might need to), You can make it call a helper function that defines the outer class of the field, and the helper function accomplishes the actual function:
Template<typename t>const rational<t> domultiply (const rational& LHS, const rational& RHS) { ...} Template<typename t>class rational{public: operator * (const rational& LHS, const rational& RHS) { Domultiply (LHS,RHS); ...};
View Code
Many compilers require the template definition to be placed in the header file in order to implement the template, so it may be necessary to define the domultiply in the header file, but the domultiply may not be inline.
Effective C + + clause 46 When a type conversion is required, define a non-member function for the template