Heavy-duty operators and conversions-conversions and class types [lower]
Iv. Reload confirmation and real parameters of the class
When you need to convert the real parameters of a function, the compiler automatically applies the conversion operator or constructor of the class. Therefore, class conversion operators should be considered during function determination. Function overload determination consists of three steps:
1) determine the set of candidate functions: these are functions with the same name as the called functions.
2) Select a viable function: these are candidate functions that match the number and type of parameters in the function call. When selecting a feasible function, the compiler determines the need for a conversion operation.Which of the following conversion operations matches each parameter?.
3) select the best matching function. To determine the best match, convert the real parameter toType conversion for classification. A set of possible conversions for real and form parameters of the class typeIncluding class type conversion.
1. standard conversion after conversion Operators
If the two functions in the overload set can match with the same conversion function, use the level of the standard conversion sequence after or before conversion to determine which function has the best matching.
Otherwise, ifYou can use different conversion operationsThe two transformations are considered to be the same good match, regardless of the standard conversion level that may or does not need.
Note: There are only two conversion sequences.Use the same conversion operationThe standard conversion sequence after class type conversion is used as the selection standard.
2. Multiple conversions and reloads are determined.
class SmallInt{public: operator int() const { return val; } operator double() const { return val; } //...private: std::size_t val;}; void compute(int); void compute(double); void compute(long double); SmallInt si; compute(si); //Error:ambiguous
In this example, operatorint can be used to convert si and call the compute version that accepts the int parameter, or operatordouble can be used to convert si and compute (double ).
The compiler will notTry to differentiate two different class type conversions. Specifically, even if a callA standard conversion is required after the class type conversion.,The other is exact match.,The compiler still marks the call as an error.. That is:
Void compute (int); // void compute (double); void compute (long double); SmallInt si; compute (si); // Error: ambiguous, which is still incorrect
3. Explicit forced conversions eliminate ambiguity
void compute(int); void compute(double); void compute(long double); SmallInt si; compute(static_cast
(si)); //OK compute(static_cast
(si)); //OK
4. standard conversion and constructor
Class SmallInt {public: SmallInt (int = 0) ;}; class Integral {public: Integral (int = 0) ;}; void manip (const SmallInt &); void manip (const Integral &);/**: It can convert Integral to int and call the first version of manip, * It can also be used to convert SmallInt to int and call the second version of manip. */Manip (10); // Error
Even if one of the classes definesConstructors that require standard conversion for real parametersThis function call may also have ambiguity. For example, if SmallInt defines a constructor that accepts the short parameter instead of the int parameter, the function call manip (10) requires a standard conversion from int to short before using the constructor. When selecting an overloaded version of a function call, one call requires standard conversion and the other does not. This is not substantive, and the compiler does not prefer to directly construct a function, the call is still ambiguous. For example:
Class SmallInt {public: // set int to short SmallInt (short = 0) ;}; class Integral {public: Integral (int = 0 );}; void manip (const SmallInt &); void manip (const Integral &); // The call still has the ambiguity manip (10); // Error
5. Explicit constructor calls eliminate ambiguity
manip(SmallInt(10)); //OK manip(Integral(10)); //OK
[Warning !]
When calling overload functions, you need to use constructors or forced type conversion parameters. This is poor design!
// Exercise P463 14.44 class LongDouble {public: operator double () const {cout <"double! "<Endl; return val;} operator float () const {cout <" float! "<Endl; return val;} private: double val;}; int main () {LongDouble ldObj; int ex1 = ldObj; // Error float ex2 = ldObj; // OK: float}
// Exercise 14.45 class LongDouble {public: LongDouble (double); private: double val ;}; void calc (int temp) {cout <"int" <endl ;} void calc (LongDouble temp) {cout <"LongDouble" <endl;} int main () {double dval; // void calc (int) will be called ): because the standard conversion is better than the class type conversion calc (dval );}
5. overloading, conversion, and operators
The overload operator is the overload function. Use the same process as determining whether to call an overloaded function to determine which operator (built-in or class type) is applied to a given expression.
ClassX sc; int iobj = sc + 3;
There are four possibilities:
1) there is an overloaded plus operator that matches ClassX and int.
2) Conversion exists. SC and/or int values are converted to the type defined by +. If so, the expression will first use the conversion, and then apply the appropriate plus operator.
3) becauseBoth the conversion operator and+Of.
4) This expression is invalid because there is no conversion and no overload + can be used.
1. Overload determination and operators
Both member and non-member functions are possible. This fact changes the way to select a candidate function set!
The heavy-load Determination of operators follows the common three-step process (often mentioned:-D ):
1) Select candidate Functions
2) Select feasible functions, including recognitionPotential conversion sequence of each real Parameter
3) select the best matching function
2. Operator candidate Functions
In general, the set of candidate FunctionsAll functions with the same name as the used FunctionThe used function can be seen from the function call. When operators are used in expressions,Candidate functions include operatorsBuilt-in versionAndNormal non-member version. In addition, if the left operator has a class type and the class defines the overloaded version of the operator, the candidate set contains the overloaded version of the operator.
Generally, function call candidates include only member functions or non-member functions, but not both. When determining the use of operators,The non-member and member versions of operators may be candidates..
When determining the call of a specified function, it is opposite to the use of the operator,ByCall itselfDetermine the scope of the name to be considered. If you are calling an object of the class type (or using a reference or pointer of this object), you only need to consider the member functions of this class. Member and non-member functions with the same name are not overloaded. When the overload operator is used, the call itself does not tell us anything related to the scope of the operator function used. Therefore, member and non-member versions must be considered.
[Warning: conversion and operators]
Be careful when designing heavy-duty operators, conversion constructors, and conversion functions correctly. In particular, classes that define both conversion operators and overload operators are prone to ambiguity. The following empirical rules are helpful:
1) do not define conversion classes, that isIfFooAccept ClassBarObject constructor,No longer for classBarDefine to typeFooConversion Operator.
2) avoid conversion to built-in arithmetic types. Specifically, if the conversion to the arithmetic type is defined
A) do not define an overloaded version that accepts Arithmetic Operators. If you need to use these operators,The conversion operator will convert the object of your defined type,Then you can use the built-in operators.
B) do not define conversions to more than one arithmetic type.Provides standard conversions to other arithmetic types.
The simplest rule is: For those "obviously correct,Avoid defining conversion functions and limiting non-explicit Constructors.
3. conversion may lead to ambiguity of built-in Operators
Class SmallInt {public: SmallInt (int = 0); operator int () const {return val;} friend SmallInt operator + (const SmallInt &, const SmallInt &); private: std:: size_t val ;}; int main () {SmallInt s1, s2; // use the overloaded version SmallInt s3 = s1 + s2 that accepts two SmallInt values; // OK // convert 0 to SmallInt and use the + SmallInt version. // You can also convert s3 to an int value and use the built-in plus operator on the int value. Int I = s3 + 0; // Error}
[Careful mine]
Providing both a conversion function for the arithmetic type and an overloaded operator for the same class may lead to ambiguity between the overloaded operator and the built-in operator.
4. Feasible operators and transformations
By listing feasible functions for each call, you can understand the behavior of these two calls. In the first call, there are two feasible addition operators:
SmallInt operator + (const SmallInt &, const SmallInt &); built-in operator + (int, int)
The first addition does not require real parameter conversion -- s1 and s2 exactly match the type of the form parameter. The built-in addition operator needs to be converted to both real parameters. Therefore, the overload operator matches the two real parameters well, so it will be called.
For the second addition operation:
int i = s3 + 0; // error: ambiguous
The two functions are equally feasible. In this case,Reload+The version exactly matches the first real parameter.,The built-in version exactly matches the second real parameter.. The first feasible function is better for the left operand, while the second feasible function is better for the right operand. Because the best viable function is not found, the call is marked as ambiguous.
// Exercise 14.46 which operator + class Complex {public: Complex (double) ;}; class LongDouble {friend LongDouble operator + (LongDouble &, int) will be called by the last statement ); // 1 public: LongDouble (int); operator double (); LongDouble operator + (const Complex &); // 2 //..}; longDouble operator + (const LongDouble &, double); // 3int main () {LongDouble ld (16.08); double res = ld + 15.05; // call 3. Why ?}