A deep probe into the usage of function overloading in C + + _c language

Source: Internet
Author: User
Tags function prototype volatile

C + + allows the specification of multiple functions with the same name within the same scope. These functions are called overloaded functions and are described in more detail in "overloading." With overloaded functions, programmers can provide different semantics for functions based on the type and number of parameters.
For example, a task performed with a print function that takes a string (or char *) parameter is a different task than a function that takes a parameter of a "double" type. Overloading allows universal naming and allows programmers to create names, such as PRINT_SZ or Print_d. The following table shows what parts of the function declaration are used by C + + to differentiate between groups of functions that have the same name in the same scope.
Overload considerations

function Declaration Element is it used for overloading?
function return type No
Number of parameters Is
Type of parameter Is
Ellipsis exists or is missing Is
Use of a typedef name Whether
Unspecified array bounds Whether
const or volatile (see below) Is

Although functions can be differentiated based on the return type, they cannot be overloaded on this basis. They are used as the basis for overloading only when the Const or volatile is used in a class to apply to the this pointer of the class, rather than the return type of the function. In other words, the overload applies only if the const or volatile keyword follows the parameter list of a function in the declaration.
The following example illustrates how to use overloads.

Function_overloading.cpp//compile with:/EHsc #include <iostream> #include <math.h>//Prototype Three
Print functions.         int print (char *s);
Print a string.      int print (double dvalue);
Print a double. int print (double dvalue, int prec);
Print a double with a//given precision.
using namespace Std; int main (int argc, char *argv[]) {Const double d = 893094.2987; if (ARGC < 2) {//These calls to print invoke PR
Int (char *s).
Print ("This program requires one argument.");
Print ("The argument specifies the number of");
Print ("Digits precision for the second number");
Print ("printed.");
  Exit (0);
//Invoke print (double dvalue).

Print (d);
Invoke print (double dvalue, int prec).
Print (d, atoi (Argv[1]));
}//Print a string.

int print (char *s) {cout << s << endl; return Cout.good ();}
Print a double in default precision. int print (double dvalue) {cout << dvalue << Endl; return cout. good ();
}//Print a double in specified precision. Positive numbers for precision indicate I many digits//precision after the decimal point to show.
Negative//numbers for precision indicate where to round the number/to the "the" the decimal point. int print (double dvalue, int prec) {//Use Table-lookup for rounding/truncation. Static const double rgpow10[] = {10E
-7, 10E-6, 10E-5, 10E-4, 10E-3, 10E-2, 10E-1, 10E0, 10E1, 10E2, 10E3, 10E4, 10E5, 10E6};
const int Ipowzero = 6;
If precision out of range, just print the number.
if (Prec < 6 | | | prec > 7) return print (Dvalue);
Scale, truncate, then Rescale.
Dvalue = Floor (Dvalue/rgpow10[ipowzero-prec]) * RGPOW10[IPOWZERO-PREC];
cout << dvalue << Endl;
return Cout.good ();

 }

The preceding code shows the file-scoped print function overload.
Default parameters are not considered part of the function type. Therefore, it is not used to select overloaded functions. The two functions that differ only on the default parameters are treated as multiple definitions rather than overloaded functions.
You cannot provide default parameters for overloaded operators.
parameter Matching
Select an overloaded function to implement the best match of the function declarations in the current scope with the arguments provided in the function call. If an appropriate function is found, the function is called. "Suitable" in this context has one of the following meanings:

    • An exact match was found.
    • An unimportant conversion has been performed.
    • An integral elevation has been performed.
    • A standard conversion already exists to the desired parameter type.
    • A user-defined conversion (conversion operator or constructor) already exists to the desired parameter type.
    • The argument represented by the ellipsis was found.

The compiler creates a set of candidate functions for each parameter. A candidate function is a function in which an argument can be converted to the type of a formal parameter.

Generates a set of best match functions for each parameter, and the selected function is the intersection of all sets. If the intersection contains more than one function, the overload is ambiguous and generates an error. For at least one parameter, the final selected function is always a better match than all other functions in the group. If this is not the case (if there is no clear winner), then the function call generates an error.
Consider the following declaration (marking the function as Variant 1, Variant 2, and Variant 3 for the identification in the following discussion):

Fraction &add (fraction &f, long l);    Variant 1
fraction &add (long l, fraction &f);    Variant 2
fraction &add (fraction &f, fraction &f);//Variant 3

fraction F1, F2;

Please consider the following statement:

F1 = Add (F2, 23);

The preceding statement generates two sets:

Set 1: Candidate functions whose first argument is of type fraction Set 2: A candidate function whose second argument can be converted to type int
Variant 1 Variant 1 (you can convert int to long using standard conversions)
Variant 3

A function in Set 2 is a function that has an implicit conversion of the argument type to the parameter type, in which the "cost" of a function's argument type to its formal parameter type is the lowest.
The intersection of these two sets is Variant 1. An example of an ambiguous function call is:

F1 = ADD (3, 6);

The preceding function call generates the following set:

Set 1: Candidate functions whose first argument is of type int Set 2: A candidate function with the type int for the second argument
Variant 2 (you can convert int to long using standard conversions) Variant 1 (you can convert int to long using standard conversions)

Note that the intersection between these two sets is empty. Therefore, the compiler generates an error message.
For parameter matching, functions that have n default parameters are treated as n+1 separate functions, and each function has a different number of arguments.
The ellipsis (...) is used as a wildcard, and it matches any arguments. If you do not design an overloaded set of functions with great care, this can result in many ambiguous sets.
attention
The ambiguity of an overloaded function cannot be determined until a function call is encountered. At this point, the set is generated for each parameter in the function call, and you can determine whether there is an explicit overload. This means that the ambiguity can remain in your code until they are raised by a specific function call.
parameter Type Difference
Overloaded functions distinguish between parameter types that use different initializers. Therefore, for overloads, arguments for a given type and references to that type will be treated as the same. Because they take the same initializer, they are considered to be the same. For example, Max (double, double) is treated as the same as Max (double &). Declaring two such functions can cause errors.
For the same reason, the function arguments for the type modified by const or volatile (for overloaded purposes) are treated differently from the base class.
However, the function overload mechanism can distinguish between references that are qualified by const and volatile and references to base types. This method can write code such as the following:

Argument_type_differences.cpp
//compile with:/ehsc/w3
//C4521 expected
#include <iostream>

using namespace std;
Class Over {public
: over
  () {cout << ' over default constructor\n ';}
  Over (over &o) {cout << "over&\n";}
  Over (const over &co) {cout << "const over&\n";}
  Over (volatile over &vo) {cout << "volatile over&\n";}
;

int main () {over
  O1;      Calls default constructor.
  Over O2 (O1);   Calls over (over&).
  Const over O3;   Calls default constructor.
  Over O4 (O3);   Calls over (const over&).
  volatile over O5;  Calls default constructor.
  Over O6 (O5);   Calls over (volatile over&).
}

Output

Over the default constructor over& over the
default constructor
const over& over
default Constructor
volatile over&

Pointers to the const and volatile objects are also considered to be different from pointers to base types (for overloading purposes).
parameter matching and conversion
when the compiler tries to match the actual argument against a parameter in a function declaration, if no exact match is found, it can provide either a standard conversion or a user-defined transformation to get the correct type. The converted application is subject to these rules:
Conversion sequences that contain multiple user-defined transformations are not considered.
does not consider a sequence of transformations that can be shortened by removing intermediate transformations.
The final transformation sequence (if any) is called the best match sequence. You can convert an object of type int to an object of type unsigned long in a variety of ways, as described in the standard conversion:

    • Converts from int to long and then from long to unsigned long.
    • Converts from int to unsigned long.

The first sequence (although it achieves the desired goal) is not the best match sequence-there is a shorter sequence.

The following table shows a set of transformations called commonly used transformations that have certain limitations on determining which sequence is the best match. The list in the following table discusses instances of the common transformation impact sequence selections.
Common Conversions

converting from type Convert to type
Type-name Type-name &
Type-name & Type-name
Type-name [] type-name*
Type-name (Argument-list) (*type-name) (argument-list)
Type-name Const TYPE-NAME
Type-name Volatile Type-name
type-name* Const type-name*
type-name* Volatile type-name*

The sequence in which you are trying to convert is as follows:
Exact match. The exact match between the type used to invoke the function and the type declared in the function prototype is always the best match. A sequence of commonly used conversions is categorized as an exact match. However, a sequence that does not make any conversions is considered preferable to a sequence that converts:

    • A pointer to a const (type* point to consttype*) from the pointer.
    • From the pointer to the pointer to the volatile (type* point to volatiletype*).
    • From a reference to a reference to the const (type & to Const type &).
    • From a reference to a reference to volatile (type & to volatile type &).

Use a matching promotion. Any sequence that is not categorized as containing only integer elevation, a conversion from float to double, and an exact match for a common conversion will be categorized as using an promoted match. Although the match is not as perfect, using an promoted match is still better than using a standard conversion match.

Matches using a standard transformation. A sequence that is not categorized as an exact match, or that uses a promoted match that contains only standard conversions and common conversions, is categorized as matching using standard conversions. In this category, the following rules apply:
The conversion from a pointer to a derived class to a pointer to a direct or indirect base class is better than a conversion to a void * or a const void *.
A conversion from a pointer to a derived class to a pointer to a base class produces a better match to the direct base class. Suppose the class hierarchy is shown in the following illustration.

Demo Diagram of preferred conversions
conversions from d* types to c* types are better than conversions from d* types to b* types. Similarly, conversions from d* types to b* types are better than conversions from d* type to a * type.
This same rule applies to reference conversions. Conversions from d& types to c& types are better than conversions from d& types to b& types.
This same rule applies to pointer conversions that point to members. From T D::* type to T C: Conversion of the:* type is better than from T D::* type to T B: conversion of:* type etc. (where T is the type of the member).
The preceding rule applies only along a given path that is derived. Consider the diagram shown in the following illustration.

Demonstrates multiple inheritance graphs for preferred conversions
Conversions from c* types to b* types are better than conversions from c* type to a * type. The reason is that they are located on the same path and b* closer. However, conversions from c* types to d* types are not better than conversions to a * type; there are no preferences because these transformations follow different paths.
Use a match for a user-defined transformation. This sequence cannot be categorized as an exact match, using an promoted match, or a match using a standard transformation. A sequence must contain only user-defined transformations, standard conversions, or common conversions to be categorized as matching with user-defined transformations. A match with a user-defined transformation is considered preferable to a match with an ellipsis, but not a match using a standard conversion.
Matches with ellipses. Any sequence that matches the ellipsis in the declaration is categorized as a match that uses ellipses. This is considered the weakest match.
If the built-in promotion or conversion does not exist, the user-defined conversion will apply. These transformations are selected based on the type of the matching parameter. Consider the following code:

Argument_matching1.cpp
class UDC
{public
:
  operator int ()
  {return
   0;
  }
  operator long ();
};

void Print (int i)
{
};

UDC UDC;

int main ()
{
  Print (UDC);
}

The available user-defined conversions for class UDC come from type int and long. Therefore, the compiler considers conversions for the object types that will match: UDC. The conversion to int already exists and is selected.
In the process of matching parameters, standard conversions can be applied to the results of parameters and user-defined transformations. Therefore, the following code will apply:

void Logtofile (long L);
...
UDC UDC;
Logtofile (UDC);

In the previous example, the user-defined conversion operator Long is invoked to convert UDC to type long. If a user-defined transformation with a long type is not defined, the conversion continues as follows: Converts the UDC type to an int type using a user-defined transformation. Standard conversions from type int to long are applied to match the parameters in the declaration.
If any user-defined transformations are required to match the parameters, the standard conversions are not used when calculating the best bets. This is true even if multiple candidate functions require user-defined conversions, in which case these functions are considered equal. For example:

Argument_matching2.cpp
//C2668 expected
class UDC1
{public
:
  UDC1 (int);//user-defined Conversion from int.
};

Class UDC2
{public
:
  UDC2 (long);//user-defined conversion from long.
};

void Func (UDC1);
void Func (UDC2);

int main ()
{
  Func (1);
}

All two versions of Func require user-defined conversions to convert type int to class type parameters. Possible conversions include the following:

    • Converts from an int type to a UDC1 type (user-defined conversion).
    • Converts from an int type to a long type, and then to the UDC2 type (a two-step conversion).

Even if the second transformation requires a standard conversion and a user-defined conversion, the two conversions are still considered equal.

Attention
user-defined transformations are considered to be conversions through constructors or through initialization (conversion functions). When considering best bets, two methods are considered equal.
parameter matching and this pointer
class member functions are handled in different ways, depending on whether they are declared static or not. Because a non-static function has an implicit argument that provides the this pointer, a non-static function is treated as one argument over a static function, otherwise the functions are declared in the same way.
These non-static member functions require that the implied this pointer match the object type through which the function is invoked, or for overloaded operators, they require that the first parameter match the object to which the operator applies.

Unlike other parameters in an overloaded function, when you try to match the this pointer parameter, the temporary object is not introduced and the conversion is not attempted.
When the –> member selection operator is used to access a member function, this pointer parameter has the type of class-name* Const. If you declare a member as const or volatile, the types are const class-name* const and Volatile class-name * const respectively.
. The member selection operator works in the same way, except that the implicit & (address-of) operator will be prefixed to the object name. The following example shows how this works:

Expression encountered in code
obj.name

//How the compiler treats it
(&obj)->name

The left operand of the –>* and. * (pointing pointer to member) operator is handled in relation to the processing of the parameter. The same as the –> (member selection) operator.
limit
multiple limit management acceptable set of overloaded functions:

    • Any two functions in an overloaded set of functions must have a different argument list.
    • It is an error to overload a function with the same type of argument list based on the return type only.

You cannot overload a member function based on only one static type and one non-static type.
typedef declarations do not define new types; They introduce synonyms for existing types. They do not affect overloaded mechanisms. Consider the following code:

typedef char * PSTR;

void Print (char *sztoprint);
void Print (Pstr sztoprint);

The previous two functions have the same argument list. Pstr is a synonym for type char *. In the member scope, this code generates an error.
Enumeration types are of different types and can be used to differentiate overloaded functions.
The type "array of" and "pointer to" are equivalent in terms of distinguishing overloaded functions. This condition applies only to a single dimensional degree group. Therefore, the following overloaded functions collide and generate an error message:

void Print (char *sztoprint);
void Print (char sztoprint[]);

For multidimensional arrays, the second and subsequent dimensions are treated as part of the type. Therefore, they can be used to differentiate overloaded functions:

void Print (char sztoprint[]);
void Print (char sztoprint[][7]);
void Print (char sztoprint[][9][42]);

Declaration match
any two function declarations that have the same name in the same scope can refer to two different functions of the same function or overload. If the declared argument list contains parameters of the equivalent type (as described in the previous section), the function declaration references the same function. Otherwise, they refer to the two different functions that use the overload selection.
The class scope needs to be strictly adhered to, so the function declared in the base class is not in the same scope as the function declared in the derived class. If you declare a function in a derived class with the same name as a function in the base class, the derived class function hides the base class function instead of causing the overload.
Strict adherence to the block scope is required; Therefore, the function declared in the file scope is not in the same scope as the function declared locally. If a function declared locally has the same name as a function declared in a file scope, the locally declared function hides the file-scoped function instead of causing the overload. For example:

Declaration_matching1.cpp
//compile with:/EHsc
#include <iostream>

using namespace std;
void func (int i)
{
  cout << "called file-scoped func:" << i << Endl;
}

void func (char *sz)
{
  cout << "called locally declared Func:" << sz << Endl;
}

int main ()
{
  //Declare func Local to main.
  extern void func (char *sz);

  Func (3);  C2664 Error. Func (int) is hidden.
  Func ("S");
}

The preceding code shows two definitions in the function func. Because of the char * statement, the definition of the parameter with the main type is the local definition of extern. Therefore, the definition of the parameter with type int is hidden, and the first call to Func is faulted.
for overloaded member functions, different versions of functions may gain different access rights. They are still considered to be within the bounds of the enclosing class, and therefore are overloaded functions. Consider the following code, where the member function deposit will be overloaded; one version is public and the other is private. The purpose of the
example is to provide an account class that requires the correct password to perform the deposit. Use overloads to complete this operation.
Note that calls to the account::D eposit in deposit call private member functions. This call is correct because account::D Eposit is a member function, so you can access the private members of the class.

Declaration_matching2.cpp
class account
{public
: Account
  ()
  {
  }
  double Deposit ( Double Damount, Char *szpassword);

Private:
  double deposit (double damount)
  {return
   0.0;
  }
  int Validate (char *szpassword)
  {return
   0;
  }

};

int main ()
{
  //Allocate A new object of type account.
  Account *PACCT = new Account;

  Deposit $57.22. Error:calls a private function.
  Pacct->deposit (57.22);

  Deposit $57.22 and supply a password. Ok:calls a
  //public function.
  Pacct->deposit (52.77, "pswd");
}

Double::D eposit (Double damount, char *szpassword)
{
  if (Validate (Szpassword)) return
   deposit ( Damount);
  else return
   0.0;
}

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.