Section 14.9. Conversions and Class Types


14.9. Conversions and Class Types

In Section 12.4.4 (p. 461) we saw that a nonexplicit constructor that can be called with one argument defines an implicit conversion. The compiler will use that conversion when an object of the argument type is supplied and an object of the class type is needed. Such constructors define conversions to the class type.

Exercises Section 14.8.3

Exercise 14.37:

Using the library function objects and adaptors, define an object to:

  1. Find all values that are greater than 1024.

  2. Find all strings that are not equal to pooh.

  3. Multiply all values by 2.

Exercise 14.38:

In the last call to count_if we used not1 to negate the result from bind2nd of (less_equal<int>(), 10). Why did we use not1 rather than not2.

Exercise 14.39:

Use library function objects in place of GT_cls to find the words of a specified length.


In addition to defining conversions to a class type, we can also define conversions from the class type. That is, we can define a conversion operator that, given an object of the class type, will generate an object of another type. As with other conversions, the compiler will apply this conversion automatically. Before showing how to define such conversions, we'll look at why they might be useful.

14.9.1. Why Conversions Are Useful

Assume that we want to define a class, which we'll name SmallInt, to implement safe small integers. Our class will allow us to define objects that could hold the same range of values as an 8-bit unsigned charthat is, 0 to 255. This class would catch under- and overflow errors and so would be safer to use than a built-in unsigned char.

We'd want our class to define all the same operations as are supported by an unsigned char. In particular, we'd want to define the five arithmetic operators (+, -, *, /, and %) and the corresponding compound-assignment operators; the four relational operators (<, <=, >, and >=); and the equality operators (== and !=). Evidently, we'd need to define 16 operators.

Supporting Mixed-Type Expressions

Moreover, we'd like to be able to use these operators in mixed-mode expressions. For example, it should be possible to add two SmallInt objects and also possible to add any of the arithmetic types to a SmallInt. We could come close by defining three instances for each operator:

      int operator+(int, const SmallInt&);      int operator+(const SmallInt&, int);      SmallInt operator+(const SmallInt&, const SmallInt&); 

Because there is a conversion to int from any of the arithmetic types, these three functions would cover our desire to support mixed mode use of SmallInt objects. However, this design only approximates the behavior of built-in integer arithmetic. It wouldn't properly handle mixed-mode operations for the floating-point types, nor would it properly support addition of long, unsigned int, or unsigned long. The problem is that this design converts all arithmetic types even those bigger than intto int and does an int addition.

Conversions Reduce the Number of Needed Operators

Even ignoring the issue of floating-point or large integral operands, if we implemented this design, we'd have to define 48 operators! Fortunately, C++ provides a mechanism by which a class can define its own conversions that can be applied to objects of its class type. For SmallInt, we could define a conversion from SmallInt to type int. If we define the conversion, then we won't need to define any of the arithmetic, relational, or equality operators. Given a conversion to int, a SmallInt object could be used anywhere an int could be used.

If there were a conversion to int, then

      SmallInt si(3);      si + 3.14159;         // convert si to int, then convert to double 

would be resolved by

  1. Converting si to an int.

  2. Converting the resulting int to double and adding it to the double literal constant 3.14159, yielding a double value.

14.9.2. Conversion Operators

A conversion operator is a special kind of class member function. It defines a conversion that converts a value of a class type to a value of some other type. A conversion operator is declared in the class body by specifying the keyword operator followed by the type that is the target type of the conversion:

      class SmallInt {      public:          SmallInt(int i = 0): val(i)          { if (i < 0 || i > 255)             throw std::out_of_range("Bad SmallInt initializer");          }          operator int() const { return val; }      private:          std::size_t val;      }; 

A conversion function takes the general form

      operator type(); 

where type represents the name of a built-in type, a class type, or a name defined by a typedef. Conversion functions can be defined for any type (other than void) that could be a function return type. In particular, conversions to an array or function type are not permitted. Conversions to pointer typesboth data and function pointersand to reference types are allowed.

A conversion function must be a member function. The function may not specify a return type, and the parameter list must be empty.



All of the following declarations are errors:

      operator int(SmallInt &);            // error: nonmember      class SmallInt {      public:          int operator int();              // error: return type          operator int(int = 0);           // error: parameter list          // ...      }; 

Although a conversion function does not specify a return type, each conversion function must explicitly return a value of the named type. For example, operator int returns an int; if we defined an operator Sales_item, it would return a Sales_item; and so on.

Conversion operations ordinarily should not change the object they are converting. As a result, conversion operators usually should be defined as const members.



Using a Class-Type Conversion

Once a conversion exists, the compiler will call it automatically (Section 5.12.1, p. 179) in the same places that a built-in conversion would be used:

  • In expressions:

          SmallInt si;      double dval;      si >= dval          // si converted to int and then convert to double 

  • In conditions:

          if (si)                // si converted to int and then convert to bool 

  • When passing arguments to or returning values from a function:

          int calc(int);      SmallInt si;      int i = calc(si);      // convert si to int and call calc 

  • As operands to overloaded operators:

          // convert si to int then call opeator<< on the int value      cout << si << endl; 

  • In an explicit cast:

          int ival;      SmallInt si = 3.541; //      instruct compiler to cast si to int      ival = static_cast<int>(si) + 3; 

Class-Type Conversions and Standard Conversions

When using a conversion function, the converted type need not exactly match the needed type. A class-type conversion can be followed by a standard conversion (Section 5.12.3, p. 181) if needed to obtain the desired type. For example, in the comparison between a SmallInt and a double

      SmallInt si;      double dval;      si >= dval // si converted to int and then convert to double 

si is first converted from a SmallInt to an int, and then the int value is converted to double.

Only One Class-Type Conversion May Be Applied

A class-type conversion may not be followed by another class-type conversion. If more than one class-type conversion is needed then the code is in error.



For example, assume we had another class, Integral, that could be converted to SmallInt but that had no conversion to int:

      // class to hold unsigned integral values      class Integral {      public:          Integral(int i = 0): val(i) { }          operator SmallInt() const { return val % 256; }      private:          std::size_t val;      }; 

We could use an Integral where a SmallInt is needed, but not where an int is required:

      int calc(int);      Integral intVal;      SmallInt si(intVal);  // ok: convert intVal to SmallInt and copy to si      int i = calc(si);     // ok: convert si to int and call calc      int j = calc(intVal); // error: no conversion to int from Integral 

When we create si, we use the SmallInt copy constructor. First int_val is converted to a SmallInt by invoking the Integral conversion operator to generate a temporary value of type SmallInt. The (synthesized) SmallInt copy constructor then uses that value to initialize si.

The first call to calc is also okay: The argument si is automatically converted to int, and the int value is passed to the function.

The second call is an error: There is no direct conversion from Integral to int. To get an int from an Integral would require two class-type conversions: first from Integral to SmallInt and then from SmallInt to int. However, the language allows only one class-type conversion, so the call is in error.

Standard Conversions Can Precede a Class-Type Conversion

When using a constructor to perform an implicit conversion (Section 12.4.4, p. 462), the parameter type of the constructor need not exactly match the type supplied. For example, the following code invokes the constructor SmallInt(int) defined in class SmallInt to convert sobj to the type SmallInt:

      void calc(SmallInt);      short sobj;      // sobj promoted from short to int      // that int converted to SmallInt through the SmallInt(int) constructor      calc(sobj); 

If needed, a standard conversion sequence can be applied to an argument before a constructor is called to perform a class-type conversion. To call the function calc(), a standard conversion is applied to convert dobj from type double to type int. The SmallInt(int) constructor is then invoked to convert the result of the conversion to the type SmallInt.

Exercises Section 14.9.2

Exercise 14.40:

Write operators that could convert a Sales_item to string and to double. What values do you think these operators should return? Do you think these conversions are a good idea? Explain why or why not.

Exercise 14.41:

Explain the difference between these two conversion operators:

      class Integral {      public:          const int();          int() const;      }; 

Are either of these conversions too restricted? If so, how might you make the conversion more general?

Exercise 14.42:

Define a conversion operator to bool for the CheckoutRecord class from the exercises in Section 14.2.1 (p. 515).

Exercise 14.43:

Explain what the bool conversion operator does. Is that the only possible meaning for this conversion for the CheckoutRecord type? Explain whether you think this conversion is a good use of a conversion operation.


14.9.3. Argument Matching and Conversions

The rest of this chapter covers a somewhat advanced topic. It can be safely skipped on first reading.



Class-type conversions can be a boon to implementing and using classes. By defining a conversion to int for SmallInts, we made the class easier to implement and easier to use. The int conversion lets users of SmallInt use all the arithmetic and relational operators on SmallInt objects. Moreover, users can safely write expressions that intermix SmallInts and other arithmetic types. The class implementor's job is made much easier by defining a single conversion operator instead of having to define 48 (or more) overloaded operators.

Class-type conversions can also be a great source of compile-time errors. Problems arise when there are multiple ways to convert from one type to another. If there are several class-type conversions that could be used, the compiler must figure out which one to use for a given expression. In this section, we look at how class-type conversions are used to match an argument to its corresponding parameter. We look first at how parameters are matched for functions that are not overloaded and then look at overloaded functions.

Used carefully, class-type conversions can greatly simplify both class and user code. Used too freely, they can lead to mysterious compile-time errors that can be hard to understand and hard to avoid.



Argument Matching and Multiple Conversion Operators

To illustrate how conversions on values of class type interact with function matching, we'll add two additional conversions to our SmallInt class. We'll add a second constructor that takes a double and also define a second conversion operator to convert SmallInt to double:

      // unwise class definition:      // multiple constructors and conversion operators to and from the built-in types      // can lead to ambiguity problems      class SmallInt {      public:          // conversions to SmallInt from int and double          SmallInt(int = 0);          SmallInt(double);          // Conversions to int or double from SmallInt          // Usually it is unwise to define conversions to multiple arithmetic types          operator int() const { return val; }          operator double() const { return val; }          // ...      private:          std::size_t val;      }; 

Ordinarily it is a bad idea to give a class conversions to or from two built-in types. We do so here to illustrate the pitfalls involved.



Consider the simple case where we call a function that is not overloaded:

      void compute(int);      void fp_compute(double);      void extended_compute(long double);      SmallInt si;      compute(si);          // SmallInt::operator int() const      fp_compute(si);       // SmallInt::operator double() const      extended_compute(si); // error: ambiguous 

Either conversion operator could be used in the call to compute:

  1. operator int generates an exact match to the parameter type.

  2. operator double followed by the standard conversion from double to int matches the parameter type.

An exact match is a better conversion than one that requires a standard conversion. Hence, the first conversion sequence is better. The conversion function SmallInt::operator int() is chosen to convert the argument.

Similarly, in the second call, fp_compute could be called using either conversion. However, the conversion to double is an exact match; it requires no additional standard conversion.

The final call to extended_compute is ambiguous. Either conversion function could be used, but each would have to be followed by a standard conversion to get to long double. Hence, neither conversion is better than the other, so the call is ambiguous.

If two conversion operators could be used in a call, then the rank of the standard conversion (Section 7.8.4, p. 272), if any, following the conversion function is used to select the best match.



Argument Matching and Conversions by Constructors

Just as there might be two conversion operators, there can also be two constructors that might be applied to convert a value to the target type of a conversion.

Consider the manip function, which takes an argument of type SmallInt:

      void manip(const SmallInt &);      double d; int i; long l;      manip(d);     // ok: use SmallInt(double) to convert the argument      manip(i);     // ok: use SmallInt(int) to convert the argument      manip(l);     // error: ambiguous 

In the first call, we could use either of the SmallInt constructors to convert d to a value of type SmallInt. The int constructor requires a standard conversion on d, whereas the double constructor is an exact match. Because an exact match is better than a standard conversion, the constructor SmallInt(double) is used for the conversion.

In the second call, the reverse is true. The SmallInt(int) constructor provides an exact matchno additional conversion is needed. To call the SmallInt constructor that takes a double would require that i first be converted to double. For this call, the int constructor would be used to convert the argument.

The third call is ambiguous. Neither constructor is an exact match for long. Each would require that the argument be converted before using the constructor:

  1. standard conversion (long to double) followed by SmallInt(double)

  2. standard conversion (long to int) followed by SmallInt(int)

These conversion sequences are indistinguishable, so the call is ambiguous.

When two constructor-defined conversions could be used, the rank of the standard conversion, if any, required on the constructor argument is used to select the best match.



Ambiguities When Two Classes Define Conversions

When two classes define conversions to each other, ambiguities are likely:

      class Integral;      class SmallInt {      public:          SmallInt(Integral); // convert from Integral to SmallInt          // ...       };      class Integral {      public:          operator SmallInt() const; // convert from SmallInt to Integral          // ...       };      void compute(SmallInt);      Integral int_val;      compute(int_val);  // error: ambiguous 

The argument int_val can be converted to a SmallInt in two different ways. The compiler could use the SmallInt constructor that takes an Integral object or it could use the Integral conversion operation that converts an Integral to a SmallInt. Because these two functions are equally good, the call is in error.

In this case, we cannot use a cast to resolve the ambiguitythe cast itself could use either the conversion operation or the constructor. Instead, we would need to explicitly call the conversion operator or the constructor:

      compute(int_val.operator SmallInt());   // ok: use conversion operator      compute(SmallInt(int_val));             // ok: use SmallInt constructor 

Moreover, conversions that we might think would be ambiguous can be legal for what seem like trivial reasons. For example, our SmallInt class constructor copies its Integral argument. If we change the constructor so that it takes a reference to const Integral

      class SmallInt {      public:      SmallInt(const Integral&);      }; 

our call to compute(int_val) is no longer ambiguous! The reason is that using the SmallInt constructor requires binding a reference to int_val, whereas using class Integral's conversion operator avoids this extra step. This small difference is enough to tip the balance in favor of using the conversion operator.

The best way to avoid ambiguities or surprises is to avoid writing pairs of classes where each offers an implicit conversion to the other.



Caution: Avoid Overuse of Conversion Functions

As with using overloaded operators, judicious use of conversion operators can greatly simplify the job of a class designer and make using a class easier. However, there are two potential pitfalls: Defining too many conversion operators can lead to ambiguous code, and some conversions can be confusing rather than helpful.

The best way to avoid ambiguities is to ensure that there is at most one way to convert one type to another. The best way to do that is to limit the number of conversion operators. In particular there should be only one conversion to a built-in type.

Conversion operators can be misleading when they are used where there is no obvious single mapping between the class type and the conversion type. In such cases, providing a conversion function may be confusing to the user of the class.

As an example, if we had a class that represented a Date, we might think it would be a good idea to provide a conversion from Date to int. However, what value should the conversion function return? The function might return the julian date, which is the sequence number of the current date starting from 0 as January 1. But should the year precede the day or follow it? That is, would January 31, 1986 be represented as 1986031 or 311986? Alternatively, the conversion operator might return an int representing the day count since some epoch point. The counter might count days since January 1, 1971 or some other starting point.

The problem is that whatever choice is made, the use of Date objects will be ambiguous because there is no single one-to-one mapping between an object of type Date and a value of type int. In such cases, it is better not to define the conversion operator. Instead, the class ought to define one or more ordinary members to extract the information in these various forms.


14.9.4. Overload Resolution and Class Arguments

As we have just seen, the compiler automatically applies a class conversion operator or constructor when needed to convert an argument to a function. Class conversion operators, therefore, are considered during function resolution. Function overload resolution (Section 7.8.2, p. 269) consists of three steps:

1.

Determine the set of candidate functions: These are the functions with the same name as the function being called.

2.

Select the viable functions: These are the candidate functions for which the number and type of the function's parameters match the arguments in the call. When selecting the viable functions, the compiler also determines which conversion operations, if any, are needed to match each parameter.

3.

The best match function is selected. To determine the best match, the type conversions needed to convert argument(s) to the type of the corresponding parameter(s) are ranked. For arguments and parameters of class type, the set of possible conversions includes class-type conversions.

Standard Conversions Following Conversion Operator

Which function is the best match can depend on whether one or more class-type conversions are involved in matching different functions.

If two functions in the overload set can be matched using the same conversion function, then the rank of the standard conversion sequence that follows or precedes the conversion is used to determine which function has the best match.


Otherwise, if different conversion operations could be used, then the conversions are considered equally good matches, regardless of the rank of any standard conversions that might or might not be required.


On page 541 we looked at the effect of class-type conversions on calls to functions that are not overloaded. Now, we'll look at similar calls but assume that the functions are overloaded:

      void compute(int);      void compute(double);      void compute(long double); 

Assuming we use our original SmallInt class that only defines one conversion operatorthe conversion to intthen if we pass a SmallInt to compute, the call is matched to the version of compute that takes an int.

All three compute functions are viable:

  • compute(int) is viable because SmallInt has a conversion to int. That conversion is an exact match for the parameter.

  • compute(double) and compute(long double) are also viable, by using the conversion to int followed by the appropriate standard conversion to either double or long double.

Because all three functions would be matched using the same class-type conversion, the rank of the standard conversion, if any, is used to determine the best match. Because an exact match is better than a standard conversion, the function compute(int) is chosen as the best viable function.

The standard conversion sequence following a class-type conversion is used as a selection criterion only if the two conversion sequences use the same conversion operation.



Multiple Conversions and Overload Resolution

We can now see one reason why adding a conversion to double is a bad idea. If we use the revised SmallInt class that defines conversions to both int and double, then calling compute on a SmallInt value is ambiguous:

      class SmallInt {      public:          // Conversions to int or double from SmallInt          // Usually it is unwise to define conversions to multiple arithmetic types          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 case we could use the operator int to convert si and call the version of compute that takes an int. Or we could use operator double to convert si and call compute(double).

The compiler will not attempt to distinguish between two different class-type conversions. In particular, even if one of the calls required a standard conversion following the class-type conversion and the other were an exact match, the compiler would still flag the call as an error.

Explicit Constructor Call to Disambiguate

A programmer who is faced with an ambiguous conversion can use a cast to indicate explicitly which conversion operation to apply:

      void compute(int);      void compute(double);      SmallInt si;      compute(static_cast<int>(si)); // ok: convert and call compute(int) 

This call is now legal because it explicitly says which conversion operation to apply to the argument. The type of the argument is forced to int by the cast. That type exactly matches the parameter of the first version of compute that takes an int.

Standard Conversions and Constructors

Let's look at overload resolution when multiple conversion constructors exist:

      class SmallInt {      public:          SmallInt(int = 0);      };      class Integral {      public:          Integral(int = 0);      };      void manip(const Integral&);      void manip(const SmallInt&);      manip(10); // error: ambiguous 

The problem is that both classes, Integral and SmallInt, provide constructors that take an int. Either constructor could be used to match a version of manip. Hence, the call is ambiguous: It could mean convert the int to Integral and call the first version of manip, or it could mean convert the int to a SmallInt and call the second version.

This call would be ambiguous even if one of the classes defined a constructor that required a standard conversion for the argument. For example, if SmallInt defined a constructor that took a short instead of an int, the call manip(10) would require a standard conversion from int to short before using that constructor. The fact that one call requires a standard conversion and the other does not is immaterial when selecting among overloaded versions of a call. The compiler will not prefer the direct constructor; the call would still be ambiguous.

Explicit Constructor Call to Disambiguate

The caller can disambiguate by explicitly constructing a value of the desired type:

      manip(SmallInt(10));    // ok: call manip(SmallInt)      manip(Integral(10));    // ok: call manip(Integral) 

Needing to use a constructor or a cast to convert an argument in a call to an overloaded function is a sign of bad design.



14.9.5. Overloading, Conversions, and Operators

Overloaded operators are overloaded functions. The same process that is used to resolve a call to an overloaded function is used to determine which operator built-in or class-typeto apply to a given expression. Given code such as

      ClassX sc;      int iobj = sc + 3; 

Exercises Section 14.9.4

Exercise 14.44:

Show the possible class-type conversion sequences for each of the following initializations. What is the outcome of each initialization?

      class LongDouble {          operator double();          operator float();      };      LongDouble ldObj;      (a) int ex1 = ldObj;    (b) float ex2 = ldObj; 

Exercise 14.45:

Which calc() function, if any, is selected as the best viable function for the following call? Show the conversion sequences needed to call each function and explain why the best viable function is selected.

      class LongDouble {      public          LongDouble(double);          // ...      };      void calc(int);      void calc(LongDouble);      double dval;      calc(dval); // which function? 


there are four possibilities:

  • There is an overloaded addition operator that matches ClassX and int.

  • There are conversions to convert sc and/or to convert an int to types for which + is defined. If so, this expression will use the conversion(s) followed by applying the appropriate addition operator.

  • The expression is ambiguous because both a conversion operator and an overloaded version of + are defined.

  • The expression is invalid because there is neither a conversion nor an over-loaded + to use.

Overload Resolution and Operators

The fact that member and nonmember functions are possible changes how the set of candidate functions is selected.



Overload resolution (Section 7.8.2, p. 269) for operators follows the usual three-step process:

1.

Select the candidate functions.

2.

Select the viable functions including identifying potential conversions sequences for each argument.

3.

Select the best match function.

Candidate Functions for Operators

As usual, the set of candidate functions consists of all functions that have the name of the function being used, and that are visible from the place of the call. In the case of an operator used in an expression, the candidate functions include the built-in versions of the operator along with all the ordinary nonmember versions of that operator. In addition, if the left-hand operand has class type, then the candidate set will contain the overloaded versions of the operator, if any, defined by that class.

Ordinarily, the candidate set for a call includes only member functions or nonmember functions but not both. When resolving the use of an operator, it is possible for both nonmember and member versions of the operator to be candidates.



When resolving a call to a named function (as opposed to the use of an operator), the call itself determines the scope of names that will be considered. If the call is through an object of a class type (or through a reference or pointer to such an object), then only the member functions of that class are considered. Member and nonmember functions with the same name do not overload one another. When we use an overloaded operator, the call does not tell us anything about the scope of the operator function that is being used. Therefore, both member and nonmember versions must be considered.

Caution: Conversions and Operators

Correctly designing the overloaded operators, conversion constructors, and conversion functions for a class requires some care. In particular, ambiguities are easy to generate if a class defines both conversion operators and overloaded operators. A few rules of thumb can be helpful:

  1. Never define mutually converting classesthat is, if class Foo has a constructor that takes an object of class Bar, do not give class Bar a conversion operator to type Foo.

  2. Avoid conversions to the built-in arithmetic types. In particular, if you do define a conversion to an arithmetic type, then

    • Do not define overloaded versions of the operators that take arithmetic types. If users need to use these operators, the conversion operation will convert objects of your type, and then the built-in operators can be used.

    • Do not define a conversion to more than one arithmetic type. Let the standard conversions provide conversions to the other arithmetic types.

The easiest rule of all: Avoid defining conversion functions and limit nonexplicit constructors to those that are "obviously right."


Conversions Can Cause Ambiguity with Built-In Operators

Let's extend our SmallInt class once more. This time, in addition to a conversion operator to int and a constructor from int, we'll give our class an overloaded addition operator:

      class SmallInt {      public:          SmallInt(int = 0); // convert from int to SmallInt          // conversion to int from SmallInt          operator int() const { return val; }          // arithmetic operators          friend SmallInt          operator+(const SmallInt&, const SmallInt&);      private:           std::size_t val;      }; 

Now we could use this class to add two SmallInts, but we will run into ambiguity problems if we attempt to perform mixed-mode arithmetic:

      SmallInt s1, s2;      SmallInt s3 = s1 + s2;         // ok: uses overloaded operator+      int i = s3 + 0;                // error: ambiguous 

The first addition uses the overloaded version of + that takes two SmallInt values. The second addition is ambiguous. The problem is that we could convert 0 to a SmallInt and use the SmallInt version of +, or we could convert s3 to int and use the built-in addition operator on ints.

Providing both conversion functions to an arithmetic type and over-loaded operators for the same class type may lead to ambiguities between the overloaded operators and the built-in operators.



Viable Operator Functions and Conversions

We can understand the behavior of these two calls by listing the viable functions for each call. In the first call, there are two viable addition operators:

  • operator+(const SmallInt&, const SmallInt&)

  • The built-in operator+(int, int)

The first addition requires no conversions on either argument s1 and s2 match exactly the types of the parameters. Using the built-in addition operator for this addition would require conversions on both arguments. Hence, the overloaded operator is a better match for both arguments and is the one that is called. For the second addition

      int i = s3 + 0;          // error: ambiguous 

the same two functions are viable. In this case, the overloaded version of + matches the first argument exactly, but the built-in version is an exact match for the second argument. The first viable function is better for the left operand, whereas the second viable function is better for the right operand. The call is flagged as ambiguous because no best viable function can be found.

Exercises Section 14.9.5

Exercise 14.46:

Which operator+, if any, is selected as the best viable function for the addition operation in main? List the candidate functions, the viable functions, and the type conversions on the arguments for each viable function.

      class Complex {          Complex(double);          // ...      };      class LongDouble {          friend LongDouble operator+(LongDouble&, int);      public:          LongDouble(int);          operator double();          LongDouble operator+(const complex &);          // ...       };      LongDouble operator+(const LongDouble &, double);      LongDouble ld(16.08);      double res = ld + 15.05; // which operator+ ? 




C++ Primer
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2006
Pages: 223
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net