Draft 2002-10-11

Chapter 6

Functions

Every C++ program has at least one function (named main), and all but the most trivial programs define additional functions. The C++ standard library provides numerous functions that your program can call. This chapter discusses how to declare, define, and call functions. A function declaration tells the compiler about a function's name, return type, and parameters. A function definition also provides the body of the function.

See Chapter 4 for more information about function call expressions, Chapter 5 for information about statements, which make up function bodies, Chapter 7 for information about member functions, and Chapter 8 for information on function templates.

Function Declarations

A function declaration tells the compiler about a function name and how to call the function. The actual body of the function can be defined separately, as described later in this chapter. A function declaration has the following parts

type name ( parameters ) cv-qualifiers except-spec ;

The parameters, cv-qualifiers, and except-spec are optional. The type is required, except for constructors, destructors, and type conversion operators. The name is the function name. Each of these parts is described in the following sections. Example 6-1 shows a variety of function declarations.

Example 6-1: Declaring functions.

// Function named "add", which returns type int, and
// takes two parameters, each of type int. The names a and b
// are optional.
int add(int a, int b);

// Function named "print", which takes a const string
// reference, and does not return anything. The function is
// expanded inline.
inline void print(const std::string& str)
{
  std::cout << str;
}

// Function named "test", which takes two floating point
// arguments and return an enumeration. This function
// does not throw any exceptions.
enum fptest { less=-1, equal, greater };
fptest test(double, double) throw();

class demo {
public:
  // Member function named "value", returning type int,
  // taking no arguments, and is const, which means it
  // can be called for a const object.
  int value() const;
  // Function named "~demo", that is, a destructor,
  // that is virtual. Constructors and destructors do not
  // have return types. Destructors do not take arguments.
  virtual ~demo();
  // Inline, overloaded, const type conversion operator.
  operator bool() const { return value() != 0; }
};

Return Type

The type is the function's return type. It is a list of type specifiers (see Chapter 3) and can include any of the following function specifiers. Function specifiers can be mixed freely with other type specifiers, but the convention is to list function specifiers before other type specifiers.

explicit
Applies only to constructors. An explicit constructor cannot be used in an implicit type conversion. See Chapter 7 for details.
inline
Tells the compiler to expand the function body at the point of the function call. An inline function must be defined in every file where it is used, and the definition must be identical in every file. Local static objects, including string literals, in the function's body refer to the same object in every file.
A function definition in a class declaration is an inline function definition, even without the use of the inline specifier.
The inline specifier is a hint to the compiler, and the compiler is free to ignore the hint. Most compilers impose a variety of restrictions on which functions can be expanded inline. The restrictions vary from one compiler to another. For example, some compilers refuse to expand any function that contains a loop statement. Most compilers cannot expand a recursive function.
Inline functions are most often used for extremely simple functions. For example, all standard containers have a member function empty(), which returns true if the container is empty. Some containers might implement the function as the following inline function:
inline bool empty() const { return size() != 0; }
virtual
Applies only to non-static member functions. A virtual function is one where the function definition is bound to the function call at runtime, instead of at compile time. See Chapter 7 for details.

If the return type is void, no value is returned to the caller. The function does not need a return statement. The form return; is permitted, or the return expression must have type void.

If the return type is anything other than void, every return statement must have an expression whose type can be implicitly converted to the function's return type.

The return type cannot be an array or function type, but can be a pointer or reference to an array or function.

In a function declaration (but not a definition), you can use the extern storage class specifier. Declarations and definitions can have linkage specifications, e.g., extern "C". See Chapter 3 for more information.

A friend specifier can be used to declare a friend function. See Chapter 7 for more information.

Note that the return type is not considered in overload resolution. See Overloading, later in this chapter, for details.

Parameters

The function parameters are optional. If the function takes no parameters, you can leave the parentheses empty or use the keyword void. If the function requires parameters, the parameter list is comma-separated, where each parameter is a simple declaration of the following form:

type-specifiers declarator = expr

where the declarator is optional. If present, it can optionally have pointer and reference operators and array dimensions. The expr part is also optional; if omitted, the = symbol is also omitted. The type-specifiers permit the optional register and auto storage class specifiers. See Chapter 3 for more information about declarators and specifiers.

Note

In C, a function that takes no arguments requires the void keyword, but in C++, void is optional. Thus, void appears most often in headers that must be used in both C and C++ programs. For example,

#ifdef __cplusplus
  #define EXTERN extern "C"
#else
  #define EXTERN extern
#endif
EXTERN int get_version(void);

Note

In other situations, you can use whichever style you prefer.

You can omit the parameter name from the declarator. In a function declaration, the name is important only to the human reader. In a function definition, a nameless parameter tells the reader that the parameter is not used in the function body. For example, suppose a graphics package defines a variety of shape classes, all deriving from a common base class, shape. Among the operations permitted on a shape is scale, which takes two arguments: the scaling amounts in the x and y directions. Suppose further that the square shape (unlike rectangle) heeds only the x scale factor. The square::scale function might be written as follows:

void square::scale(double xscale, double)
{
  this->size *= xscale;
}

A parameter can have cv-qualifiers (const and volatile). The qualifiers have their usual meaning (Chapter 3) in the function body. The qualifiers and storage class specifier are not part of the function type and do not participate in overload resolution.

Default Arguments

A parameter can have a default argument (separated from the declarator by an equal sign). Only the right-most parameters can have default arguments. If any parameter does not have a default argument, all parameters to the left cannot have default arguments. The default parameter can be any expression. (If you want to use a comma operator, enclose the expression in parentheses.)

In a function call, arguments can be omitted, starting on the right. For each omitted parameter, the default argument is substituted in the function call. Each default argument is implicitly converted to the parameter type, using the same rules that govern initializers in declarations. The default argument expressions are evaluated every time the argument is needed in a function call. Names used in the default arguments are looked up at the point of declaration, not the point of use, as shown in Example 6-2.

Example 6-2: Declaring and using default arguments

#include <iostream>
#include <ostream>

namespace demo {
  int f()
  {
    return 20;
  }
}

int f()
{
  return 10;
}

// The default argument for y is always the global f(),
// even if a different f() is visible where func()
// is called.
int func(int x, int y = f())
{
  return x + y;
}

int main()
{
  using demo::f;
  std::cout << f() << '\n';       // prints 20
  std::cout << func(32) << '\n';  // prints 42
}

Default arguments are cumulative in multiple declarations of the same function in the same scope. Later declarations can provide default arguments for additional parameters, in which case, the declaration must omit the default arguments for parameters that already have default arguments, as shown in Example 6-3.

Example 6-3: Accumulating default arguments.

void func(int x, int y);
void func(int x, int y = 10);
void func(int x = 20, int y);
void other()
{
  func(); // same as func(20, 10)
}

Different scopes can have different default arguments. For example, the source file where a function is defined might have different default arguments than those used in function declarations where the function is used. Most of the time, though, different default arguments are indicative of programmer errors.

In a member function declaration, you cannot use a nonstatic data member as a default argument, unless it refers to a specific instance. If you want to use the value of a data member as the default value for a parameter, use an overloaded function, as shown in Example 6-4.

Example 6-4: Default arguments in member functions.

class example {
public:
  void func(int x, int y = data_); // error

  // Achieve the desired effect with overloaded functions.
  void func(int x, int y);
  void func(int x)         { func(x, data_); }
private:
  int data_;
};

Read more about overloaded functions later in this chapter.

Variable Argument Lists

The last parameter can be an ellipsis (...). The comma that separates the next-to-last parameter from the ellipsis is optional. (If portability with C is important, be sure to include the comma.) The ellipsis permits a variable number of arguments in a function call. See <cstdarg> in Chapter 13 to learn how to access the additional arguments. You can use an ellipsis as the sole parameter, but there is no mechanism in standard C++ to access the arguments from the function body. Such a declaration might be used for an external function, though.

Cv-qualifiers

Only non-static member functions (but not constructors or destructors) can have cv-qualifiers (const and volatile). They are optional, and if used, they apply to the implicit object parameter of the member function (this). You can use const, volatile, neither, or both in any order. The cv-qualifiers appear after the closing parenthesis of the function parameters. The qualifiers are part of the function type, and participate in overload resolution, so you can have multiple functions with the same name and parameters, but with different qualifiers (but only if you do not also have a static member function of the same name and parameters, see the Overloading section, later in this chapter, for details).

A pointer to member function and a function typedef can also have cv-qualifiers. Only a top-level typedef can have cv-qualifiers; you cannot declare a typedef that combines a function typedef and a qualifier.

The most common use of qualifiers is to declare const member functions. These functions can be called for a const object. In general, member functions that do not change *this should be declared const. See Chapter 7 for more information on how cv-qualifiers affect member functions. Example 6-5 shows some simple uses of qualifiers.

Example 6-5: Using qualifiers with member functions.

class point
{
public:
  point(int x, int y) : x_(x), y_(y) {}
  int x()      const   { return x_; }
  int y()      const   { return y_; }
  double abs() const   { return sqrt(x()*x() + y()*y()); }
  void offset(const point& p) {
    // Cannot be const because offset() modifies x_ and y_.
    x_ += p.x();
    y_ += p.y();
  }
private:
  int x_, y_;
};

Exception Specification

An exception specification is optional, and they are in fact rarely used. The syntax is as follows:

throw ( type-list )

Where the type-list is optional. If present, it is a comma-separated list of type names. (See Chapter 3 for details about type names.) Each type name is an exception type that the function can throw. If the function throws an exception that is not listed in the exception specification, the unexpected() function is called.

The default implementation of unexpected() calls terminate() to terminate the program. You can set your own unexpected() handler, which must call terminate() or throw an exception. If it throws an exception that is not listed in the function's exception specification, bad_exception is thrown. If bad_exception is not listed in the function's exception specification, terminate() is called. In other words, if there is an exception specification, only exceptions of the listed types (or derived from one of the listed types) can be thrown from the function, or else the program terminates. See <exception> in Chapter 13 for details.

An overridden virtual function must have an exception specification that lists only types that are listed in the base class exception specifications. In particular, if the base class function does not throw any exceptions, the derived class function must not throw any exceptions.

The most common use for an exception specification is to mark functions that do not throw exceptions at all (throw()). Example 6-6 shows various uses of exception specifications.

Example 6-6: Declaring exception specifications.

class base {
public:
  virtual void f() throw();
  virtual void g(); // can throw anything
  virtual void h() throw(std::string);
};

class derived : public base {
public:
  virtual void f() throw();    // okay: same as base
  virtual void g() throw(int); // okay: subset of base
  virtual void h() throw(int); // error: int not in base
};

class more : public derived {
public:
  virtual void f();            // error: can throw anything
  virtual void g() throw();    // okay
};

// Function does not throw any exceptions.
int noproblem(int x, int y) throw()
try
{
  dostuff(x);
  dostuff(y);
}
catch(...)
{
  return 0;
}

derived* downcast(base* b) throw(std::bad_cast)
{
  return dynamic_cast<derived*>(b);
}

Note

Java programmers should note two significant differences between C++ and Java:

Declarations and Definitions

Every function must be declared or defined earlier in the source file than where it is used. For functions defined in libraries or other source files, the convention is to declare the function in a .h file and #include the .h file where the function is used.

Function Types and Signatures

A function type includes the language linkage, return type, parameter types, and cv-qualifiers. Note that for each parameter, only its type is significant; its name, storage class, and cv-qualifiers are not part of the function type. The exception specification is not part of the function type.

A single source file can have multiple declarations of the same function type, even if those declarations differ in other ways. For example, one declaration can declare a parameter const and another might declare it volatile. The qualifiers are ignored for the function type. They matter only in the function definition, as shown in Example 6-7.

Example 6-7: Declaring and defining functions.

// Three declarations of the same function type
int add(const int a, const int b);
int add(int x, volatile int);
int add(signed int, int signed);

// Definition of the function. The parameter qualifiers
// in the declarations are irrelevant. Only those in the
// definition matter.
int add(int x, int y)
{
  return x + y;
}

Array and pointer parameters types are also equivalent. In the function body, the parameter is a pointer, not an array. The array size in the parameter declaration is ignored. The first (left-most) size in a multidimensional array is ignored. Similarly, a function parameter is the same as a function pointer parameter. The rules for function types apply recursively to function and function pointer parameters. Thus, a parameter whose type is a pointer to a function that takes an int is equivalent to one where the function parameter has a const int parameter. Example 6-8 illustrates equivalent pointer types.

Example 6-8: Equivalent pointer types.

int first(const int array[10]); // size is ignored
int first(const int array[]);   // equivalent declaration
int first(const int* array);    // equivalent declaration
int first(const int* array)     // definition
{
  return array[0];
}

int apply(int func(int), int arg);
int apply(int func(const int), int);         // equivalent
int apply(int (*func)(int), int arg);        // equivalent
int apply(int (*func)(const int), int arg)   // definition
{
  return func(arg);
}

Because typedef declarations do not create new types, but only synonyms for existing types, parameters that differ only in the use of typedef types are equivalent, as shown in Example 6-9.

Example 6-9: Using typedef in equivalent parameter types.

typedef int INT;
typedef const int CINT;
void func(int);
void func(INT);          // equivalent
void func(const INT);    // equivalent
void func(CINT);         // equivalent
void func(signed int i)  // definition
{
  std::cout << i;
}

You can declare a function type from a typedef, but you cannot use the typedef in a function definition. For example,

typedef int func(int, int);
func add;             // declares int add(int, int);
int add(int a, int b) // cannot use "func add" here
{
  return a + b;
}

Be careful to distinguish between a function type and a function pointer. A function can be implicitly converted to a function pointer, and a function can be called using a function pointer. A function pointer, however, cannot be used to declare a function. For example,

typedef func* funcptr; // pointer-to-function type
funcptr a = add;       // pointer-to-function object
int i = a(1, 2);       // call the function that a points to

A function signature is similar to a function type, but it ignores the return type. Overload resolution relies on function signatures to determine which overloaded function to call. Read more about overloading later in this chapter.

Function Bodies and Try Blocks

A function definition consists of a function declaration followed by a function body. The function body has one of two forms:

compound-statement
Is executed when the function is called. When execution reaches the end of the statement, the function returns.
try ctor-initializers compound-statement handlers
Sets up a try block that includes the constructor initializers. If an exception is thrown from any of the ctor-initializers, it is handled by the handlers in the same manner as any exception thrown from the compound statement. See Chapter 7 for more information about constructor initializers.
The constructor initializers are optional. Without them, this form can be used for any function. It is equivalent to having a try statement around the entire function body. Example 6-10 shows this use of a try function body.

Example 6-10: Using a try function body.

void func1()
try {
  // statements
} catch (...) {
  // handler
}

void func2()
{
  try {
    // statements
  } catch(...) {
    // handler
  }
}

Overloading

A single function name can have multiple declarations. Two declarations with the same function type declare the same function. Declarations with different function types but the same name overload the function name with multiple functions. A function call to an overloaded function requires the compiler to resolve the overloaded name and decide which function to call. It uses the argument types (but not the return type) to resolve overloaded functions. Example 6-11 shows simple examples of overloaded functions. This section explains the rules for overloading and resolution.

Example 6-11: Overloading functions.

int sqrt(int);
double sqrt(double);

int main()
{
  std::cout << sqrt(3) << '\n';    // sqrt(int)
  std::cout << sqrt(3.14) << '\n'; // sqrt(double)
}

Declaring Overloaded Functions

Anytime you declare more than one function with the same name in the same scope, you are overloading the function name. The function can be an ordinary function, member function, constructor, or overloaded operator.

Overloaded functions must differ in their parameter lists: they must have a different number of parameters, or the parameter types must be different. Refer to the Function Types section, earlier in this chapter, for information on equivalent parameter types.

Default arguments are not considered when declaring overloaded functions (but are important when resolving a call to an overloaded function, as described in the next section). Example 6-12 shows some overloaded functions.

Example 6-12: Overloading functions.

class point {
public:
  point(int x = 0);        // overloaded constructors
  point(int x, int y);
  point(const point& pt);
  // x and y are overloaded
  int x()    const { return x_; }
  void x(int newx) { x_ = newx; }
  int y()    const { return y_; }
  void y(int newy) { y_ = newy; }
private:
  int x_, y_;
};

// add is overloaded
int    add(int, int);
int    add(int);
double add(double, double);
double add(double);
long   add(long, long);
long   add(long);

// The following declarations are not overloaded, but
// are redeclarations of the add functions
int  add(int a = 0, int b = 0);
long add(signed long, long signed);

long add(int, int); // error: cannot overload on return type

namespace ns {
  // The following is a different add function, in a
  // different scope.
  int add(int, int);
}

Calling Overloaded Functions

A function call expression (Chapter 4) must identify the function being called, based on its name and arguments. For example, the simple expression f(x) might be a call to a function named f, a function call through a function pointer named f, a function template named f, the construction of an object of class f, a conversion to a type named f, or the invocation of the function call operator of an object named f. In each situation, the compiler might use different rules for interpreting f and x.

The compiler first uses the function name and context to create a list of candidate functions. The number and types of the arguments is used to select the best match from the candidates, and that function is the one that is called. If no match is found, the compiler reports an error. If more than one match is "best," the compiler reports an ambiguity error.

For example, the C++ standard library declares three different sqrt functions (see <cmath> in Chapter 13):

float sqrt(float);
double sqrt(double);
long double sqrt(long double);

Suppose you added some other functions named sqrt, such as the following to apply sqrt to each element of a sequence:

template<typename InIter, typename OutIter>
OutIter sqrt(InIter first, InIter last, OutIter result);

In a function call to sqrt (say, sqrt(x)), the compiler first collects all the overloaded sqrt functions. That's the candidate list, which has four elements in this case. The list is then pruned to eliminate functions with the wrong number of arguments. In this case, the sqrt function template is eliminated because the expression has only one argument. Finally, the type of x is used to determine which function to call: if there is an exact match, the corresponding function is called. If x is, say, an integer, the compiler reports an error because the three floating-point sqrt functions look equally good. If x has class type, and the class has a conversion function to one of the floating point types, the compiler implicitly calls that conversion and then calls the corresponding function. For example,

struct FixedPoint {
  ...
  operator long double();
};
void demo()
{
  FixedPoint x;
  std::cout << sqrt(x) << '\n'; // prints a long double
}

If a candidate is a function template, the compiler deduces the argument types (see Chapter 8), and from that point on, the template instance is treated as a normal function.

The rules for creating the candidate list and argument list depend on the context of the function call. The argument list can also depend on context: when choosing an overloaded function to call, the member function's class is treated as an argument type.

More precisely, member functions have an implicit object parameter whose type is reference to T, where T is the class that defines the member function. Any qualifiers on the member function also qualify the type (that is, the object type for a const function is const T&). In the function body, the type of this is pointer to qualified T. (See Chapter 7 for information about this.)

A call to a member function applies to a specific object, which is the implicit object argument. With the -> and . operators, the implicit object argument is the left operand. Inside a nonstatic member function, the implicit object argument is this. The implicit object argument is considered the first argument in the argument list. Unlike normal arguments, implicit type conversions do not take place for the implicit object argument.

Table 6-1 summaries the various forms of function calls, and the following sections provide the details.

Table 6-1: Function calls and candidate lists.
Function Call Syntax Candidate Functions
expr . name ( args )expr -> name ( args ) Member functions
name ( args )expr ( args ) Member functionsNon-member functions
expr op expr Member and non-member functions
type name ( args ) Constructors
type name = expr ConstructorsType conversion operators

Qualified member function call

An expression that uses the . or -> operator to call a function must call a member function. The function name is looked up in the class of the left operand, and in its base classes (using the name lookup rules listed in Chapter 3). When a matching name is found, all matching names in that class are the candidate functions.

In other words, a derived class does not overload functions in a base class, but hides them. For a derived class to add an overloaded name to names inherited from the base class, use a using declaration in the derived class, as shown in Example 6-13.

Example 6-13: Overloading inherited functions.

#include <iostream>
#include <ostream>

class base {
public:
  void f(int) { std::cout << "f(int)\n"; }
  void g(int) { std::cout << "g(int)\n"; }

};

class derived : public base {
public:
  void f(double) { std::cout << "f(double)\n"; }
  void g(double) { std::cout << "g(double)\n"; }
  using base::g; // g(int) and g(double) are visible
};

int main()
{
  derived d;

  d.f(3);   // calls derived::f(double)
  d.g(42);  // calls base::g(int)
};

Unqualified function call

An ordinary-looking function call can be a non-member function, a member function, an object of class-type, a type name, or a variable of type pointer-to-function. For a variable of type pointer-to-function, overload resolution takes place when a value is assigned to the variable, which is discussed later in this chapter. In the other cases, the usual name lookup rules apply to find the candidate functions.

A function call in a member function searches first for matching member functions in the same class or an ancestor class. If a match is found, candidate functions are taken from the same class. If no matches are found, outer namespaces are searched for non-member functions. Example 6-14 shows how a matching member function in a base class precludes finding a better match in the global namespace.

Example 6-14: Finding candidate member functions.

#include <iostream>
#include <ostream>

void proc(int x)
{
  std::cout << "proc(int:" << x << ")\n";
}

class base {
public:
  void f(int) { std::cout << "f(int)\n"; }
  void g(int) { std::cout << "g(int)\n"; }
  void proc(double) { std::cout << "base::proc(double)\n"; }
};

class derived : public base {
public:
  void f(double) { std::cout << "f(double)\n"; }
  void g(double x) {
    std::cout << "g(double)\n";
    proc(42); // calls base::proc(double), not ::proc(int)
  }
  using base::g;
};

// Declared after derived, so call to proc() inside g()
// never sees this proc().
void proc(double x)
{
  std::cout << "proc(double:" << x << ")\n";
}

int main()
{
  derived d;
  d.g(3.14159); // calls g(double)
}

If the function call expression resolves to an object of class type, the class must have a function call operator or a conversion operator, where the conversion is to a function type: pointer to function, reference to function, or reference to pointer to function.

In the case of a conversion operator, the compiler constructs a wrapper function so that the conversion function is the first argument. The list of argument types is then the conversion type, followed by the types of the actual arguments. In other words, all the function-type conversion operators participate in overload resolution.

Example 6-15 illustrates using a class-type object as the left operand of a function call.

Example 6-15: Calling a class-type object as a function.

#include <iostream>
#include <ostream>

typedef void (*strproc)(const char*);

void print(const char* str)
{
  std::cout << "const char*:" << str << '\n';
}

void print(int x)
{
  std::cout << "int:" << x << '\n';
}

void print(double x)
{
  std::cout << "double:" << x << '\n';
}

struct simple
{
  void operator()(int x)    { print(x); } // print(int)
  void operator()(double x) { print(x); } // print(double)
};

typedef void (*intfunc)(int);
typedef void (*dblfunc)(double);

struct indirect
{
  operator intfunc() { return print; } // print(int)
  operator dblfunc() { return print; } // print(double)
  operator strproc() { return print; } // print(const char*)
};

int main()
{
  simple sim;
  indirect ind;

  sim(42);             // prints "int:42"
  sim(42.0);           // prints "double:42"
  ind(42);             // prints "int:42"
  ind(42.0);           // prints "double:42"
  ind("forty-two");    // prints "const char*:forty-two"
}

Operator

If all the operands of an operator have fundamental types, the operator has its built in meaning. If any operand has a user-defined type (class or enumeration), the programmer can overload the operator, and the overloaded operator function is selected according the usual rules of overloaded functions.

If the left operand has class type, the operator can be a non-static member function or non-member function. Otherwise, the function must be a non-member function. The operator function name is formed from the keyword operator, followed by the operator symbol, e.g., operator[], operator++, operator-. Unary operators can be member functions with no arguments or non-member functions of one argument. Binary operators are member functions of one argument or non-member functions of two arguments. Postfix increment and decrement operators are different. They are implemented as binary operators where the right operand is an int. (See Chapter 4 for more information.)

The -> operator is also different. Although it is a binary operator, it is treated as a unary operator. It must be implemented as a member function, so the function takes no arguments. It returns a pointer or another object that overloads the -> operator. Ultimately, the overloaded operators must resolve to a pointer of class type, to which the built in -> operator can be applied.

The candidate functions include all member, non-member, and built in candidate functions. Member functions do not take precedence over non-member functions.

Example 6-16 shows how operators are resolved by considering member functions and global functions, whereas named member functions take precedence over named global functions.

Example 6-16: Calling overloaded operators

class demo
{
public:
  demo(int v) : value_(v) {}

  demo add(const demo& d) const;
  demo sub(const demo& d) const;
  demo mul(const demo& d) const;
  demo operator+(const demo& d) const;
  demo operator-(const demo& d) const;
  demo operator*(const demo& d) const;
  operator int() const { return value_; }
private:
  int value_;
};

// Silly examples, but illustrative.
demo add(const demo& a) { return a; }
demo mul(const demo& a) { return a; }
demo div(const demo& a) { return a; }

demo operator+(const demo& a, const demo& b)
{
  return a.operator+(b); // force use of member function
}

demo demo::add(const demo& d)
const
{
  return *this + d; // error: calls ::operator+() or
                    //   demo::operator+()?
}

demo demo::sub(const demo& d) const
{
  return this->operator-(d); // member operator
}

demo demo::mul(const demo& d) const
{
   return ::operator*(*this, d); // global operator
}

demo demo::operator+(const demo& d) const
{
  return demo(int(*this) + int(d));
}

demo demo::operator-(const demo& d) const
{
  return sub(d); // calls demo::sub (recurses infinitely)
}

demo demo::operator*(const demo& d) const
{
  return ::mul(d); // scope operator to call global mul()
}

Function-like initialization

An object can be initialized using function-like syntax (see Chapter 3). When an object of class-type is so initialized, the candidate functions are constructors of the named class. Example 6-17 shows function-like initializers that call an overloaded constructor.

Example 6-17: Calling an overloaded constructor.

class point {
public:
  point() : x_(0), y_(0) {}
  point(int x) : x_(x), y_(0) {}
  point(int x, int y): x_(x), y_(y) {}
private:
  int x_, y_;
};

point p1(42);
point p2(4, 2);
point p3(p1);

Assignment-like initialization

An object can be initialized using assignment-like syntax (see Chapter 3). The candidate functions for "T x = i" are single-argument constructors of T, and if i has class type, conversion functions that convert i to type T, as shown in Example 6-18.

Example 6-18: Resolving assignment-like initialization.

class point {
public:
  point() : x_(0), y_(0) {}
  point(int x) : x_(x), y_(0) {}
  point(int x, int y) : x_(x), y_(y) {}
  int x() const { return x_; }
  int y() const { return y_; }
private:
  int x_, y_;
};

class dot {
public:
  dot(int x, int y) : center_(point(x, y)) {}
  dot(const point& center) : center_(center) {}
  operator point() const { return center_; }
private:
  point center_;
};

point p1 = 3;    // invokes point(int) constructor
point p2 = 4.2;  // converts 4.2 to 4, and invokes point(int)
dot   d1 = p1;   // invokes dot(const point&) constructor
point p3 = d1;   // invokes dot::operator point() and
            // implicit point(const point&) copy constructor

Conversion initialization

An object that has non-class type can be initialized with an object of class type. The candidate functions are the conversion functions of that class type. Example 6-19 shows some examples.

Example 6-19: Initializing non-class objects by calling conversion functions.

enum color { red, black };

template<typename T>
class rbnode {
public:
  rbnode(const T& value, color c);
  operator color() const { return color_; }
  operator T() const { return value_; }
private:
  T value_;
  color color_;
};

rbnode<int> n(42, black);

color c = n;
int i = n;

Reference bound to conversion

Similar to initializing an object of non-class type with an expression of class-type, as described in the previous section, you can initialize a reference to an lvalue that results from a conversion function. Most conversion operators do not return lvalues, but if they do, you can bind the results to references, as shown in Example 6-20.

Example 6-20: Binding references to conversion lvalues.

enum color { red, black };

template<typename T>
class rbnode {
public:
  rbnode(const T& value, color c);
  operator color&() { return color_; }
  operator T&()     { return value_; }
private:
  T value_;
  color color_;
};

rbnode<int> n(42, black);
color& c1 = n;
int& i1 = n;

Address of Overloaded Functions

When taking the address of a function, the compiler does not have the benefit of an argument list to help resolve overloading. Instead, it collects a list of candidate functions and picks one based on the type required by the context. The context can be an initializer, an assignment, a function parameter, the return value of a function, or an explicit type cast.

Of the potentially matching functions, non-template functions are better than template functions, and more-specialized template functions are better than less-specialized template functions. There must be exactly one remaining function, or else it is an error. Example 6-21 shows simple examples of resolving the addresses of overloaded functions.

Example 6-21: Taking the address of an overloaded function.

int abs(int);
long abs(long);
double abs(double);

template<typename T>
T abs(T x);

template<typename T, typename U>
T abs(T x, U = U());

int main()
{
  int (*intfunc)(int) = &abs;            // abs(int)
  double (*dblfunc)(double) = &abs;      // abs(double)
  float (*fltfunc)(float) = &abs;        // abs<float>
  short (*shrtfunc1)(short, int) = &abs; // abs<short,int>
  short (*shrtfunc2)(short) = &abs;      // abs<short>
}

Best Overloaded Function

To find the best matching function from the list of candidate functions, the candidate list is first pruned by removing functions with the wrong number of arguments. Then the remaining functions are checked to determine how to convert the actual arguments to the desired parameter types. The function with the simplest conversions wins. This section discusses these two steps.

Pruning the candidate list

Once the compiler has assembled its list of candidate functions, it prunes the list by removing functions that have the wrong number of arguments. If the function call has n argument expressions, functions are kept in the list if:

Also, each actual argument must be convertible (using the rules described in the next section) to the parameter type.

Choosing the best function

Of the remaining candidate functions, the "best" function is the one that is called. If more multiple functions are tied for best, it is an error. The best function is found by comparing the implicit type conversion sequences needed for each argument type (and possibly the implicit object argument). As described in the next section, some sequences are better than others. When comparing two functions, the better function is chosen as follows:

Argument conversion sequences

An actual argument is implicitly converted to the desired parameter type by undergoing a series of transformations. In rough terms, a better conversion is one where the argument type is closer to the parameter type, and therefore, the type undergoes fewer transformations.

A built in conversion sequence is better than a user-defined conversion sequence. A user-defined conversion sequence is better than matching an ellipsis parameter.

Among built in transformations, value-preserving transformations are better than those that might discard information. The transformations are as follows, in order of best to worst:

Identity, lvalue transformation, qualification adjustment
An identity transformation is "converting" a type to itself. An lvalue transformation is the conversion of an array to a pointer, an lvalue to an rvalue, or a function to a function pointer. A qualification adjustment is adding const or volatile qualifiers to match the parameter.
Promotion
The built in type promotions are listed in Chapter 4. In general, type promotions are from smaller types to larger types, so information cannot be lost.
Conversion
A built in type conversion is one that might lose information, such as converting a number to a smaller type, or floating point to integer. Certain conversions are better than other conversions:

A user-defined conversion sequence is a built in conversion sequence , followed by a user-defined conversion (constructor or type conversion operator), followed by a built in conversion sequence.

If one sequence of transformations is a subsequence of another, the shorter sequence is better than the longer one.

Among sequences that differ only by qualification, the one with fewer qualification adjustments is better than one with more adjustments.

Example 6-22 shows how overloaded functions are chosen. Notice that type promotions are preferred over the conversion to Num, even though Num has an exact type conversion to long. Also, note how an unsigned long cannot be promoted, so it must undergo a built in conversion. The compiler has no preference of int, long or double, so the conversion is ambiguous and is therefore an error. The same applies to long double; even though a human might consider the conversion to double to be "better" than conversion to int, the rules of overloading say otherwise.

Example 6-22: Resolving overloaded functions.

#include <string>

void func(double);
void func(long);
void func(int);
void func(const std::string&);

class Num {
public:
  Num(int i) : num_(i) {}
  operator long() const { return num_; }
private:
  int num_;
};

int main()
{
  short n = 42;
  func(n);           // func(int)
  func(Num(n));      // func(long)
  func(true);        // func(int)
  func(3.1415f);     // func(double)
  func("string");    // func(string);
  std::string s("string");
  func(s);           // func(string);
  func(3.14159L);    // error
  func(42UL);        // error
  func(L"widestr");  // error
}

Sidebar: Overloading vs. Default Arguments

Overloading functions can have the same effect as using default arguments. The question is when to use overloading and when to use default arguments. As usual, the answer is, "It depends."

If the default argument is complicated, you are probably better off using overloading. Using default arguments, the complicated code would be duplicated at every function call. With overloading, you can concentrate it at a single point.

If the function is complicated, you might want to use default arguments, so you can write the function only once. Even if you use overloading, write a single base function, and let the overloaded functions call the base function. For example,

class point {
public:
  // Using default arguments keeps the code
  // simple because there is only
  // one constructor.
  point(int x = 0, int y = 0);
  ...
  // set(1) sets both x and y to the same
  // value. This is difficult to do with
  // default arguments, so use overloading.
  void set(int x) { set(x, x); }
  void set(int x, int y);
};

Operator Overloading

For user-defined types (classes and enumerations), you can define alternate behavior for the operators, which is called overloading the operators. You cannot define new operator symbols, and not all operator symbols can be overloaded. Table 6-2 lists all the operator symbols, indicates which ones can be overloaded, and if so, shows whether the overload must be a non-static member function.

Table 6-2: Operators and overloading
Operator Meaning Overloading permitted? Must be member function?
+ Addition, unary plus yes no
& Address of yes no
[] Array subscript yes yes
&= Assign bitwise and yes no
^= Assign bitwise exclusive or yes no
|= Assign bitwise or yes no
-= Assign difference yes no
<<= Assign left shift yes no
= Assignment yes yes
*= Assign product yes no
/= Assign quotient yes no
%= Assign remainder yes no
>>= Assign right shift yes no
+= Assign sum yes no
& Bitwise and yes no
~ Bitwise complement yes no
^ Bitwise exclusive or yes no
| Bitwise or yes no
? : Conditional no N/A
new Create dynamic object yes no
new[] Create dynamic array yes no
-- Decrement yes no
delete Destroy dynamic object yes no
delete[] Destroy dynamic array yes no
/ Division yes no
== Equal yes no
() Function call yes yes
> Greater than yes no
>= Greater than or equal yes no
++ Increment yes no
<< Left shift yes no
< Less than yes no
<= Less than or equal yes no
&& Logical and yes no
! Logical complement yes no
|| Logical or yes no
.* Member reference no N/A
->* Member reference yes yes
. Member reference no N/A
-> Member reference yes yes
* Multiplication, dereference yes no
!= Not equal yes no
% Remainder yes no
>> Right shift yes no
:: Scope no N/A
, Serial evaluation no N/A
- Subtraction, negation yes no

An overloaded operator is a function, where the function name has the form operator, followed by the operator symbol, or in the case of a type conversion member function, a type specifier. For example,

enum logical { no, maybe, yes };
logical operator !(logical x);
logical operator &&(logical a, logical b);
operator bool(logical x);

Some overloaded operators must be member functions, and others can be member or non-member functions (as shown in Table 6-2). When you define a member function, the object is always the left-hand operand. A unary operator, therefore, takes no arguments. A binary operator takes one argument: the right-hand operand. For a non-member function, a unary operator takes one argument and a binary operator takes two arguments: the first argument is the left-hand operand, and the second is the right-hand operand.

Use overloaded operators the same way you use the built in operators. You can also use function notation, where the function name is operator followed by the operator symbol. You can use the function notation with built in operators, too. For example,

operator-(42, 10) // same as 42 - 10
operator-(33)     // same as -33

The usual rules for resolving overloaded functions applies to overloaded operators. The only difference is that the built in operators are also considered candidates. Remember that you cannot overload operators where all the operands have fundamental types. At least one operand must have a user-defined type (class or enumeration).

Note

When overloading binary operators, consider whether the operator should be commutative (a + b is the same as b + a), and if so, you might need to define two overloaded operators. For example,

enum priority { idle, low, normal, high };
// Adding an integer to a priority commutes,
// e.g., setpriority(priority() + 2);
//       setpriority(2 + priority());
priority operator+(priority p, int i);
priority operator+(int i, priority p);
// Subtracting an integer from a priority does
// not commute, e.g.,
//   setpriority(priority() - 1);
priority operator-(priority p, int i);

Short Circuit Operators

A key difference between the overloaded operators and the built in operators is that the logical && and || operators are short-circuit operators. If the expression result is known by evaluating only the left operand, the right operand is never evaluated. For overloaded operators, all operands are evaluated before the function is called, so short-circuit evaluation is impossible.

Increment and Decrement

When overloading the increment and decrement operators, they have two forms: prefix and postfix. To distinguish the two forms, the postfix form takes an additional int argument. Example 6-23 shows one way to overload the increment operator. (Decrement is analogous.)

Example 6-23: Overloading the increment operator.

enum status { stopped, running, waiting };
status& operator++(status& s) { // prefix
  if (s != waiting)
    s = status(s + 1);
  return s;
}
status operator++(status& s, int) {   // postfix
  status rtn = s;
  ++s;
  return rtn;
}
int main()
{
  status s(stopped);
  ++s;                  // calls operator++(s);
  s++;                  // calls operator++(s, 0);
}

Member Reference

The -> operator is different from the other operators. Although it is nominally a binary operator, you overload it as a unary operator. It must be implemented as a member function, so the function takes no arguments. It must return one of the following:

The chain of -> operators is followed until it ends with a pointer to a class type. The actual right operand must name a member of that class. The -> operator is most often overloaded to implement a smart pointer. See the auto_ptr<> template in the <memory> section of Chapter 13 for an example.

Function Call

The function call operator (operator()) takes any number of arguments. It must be implemented as a member function. To invoke the operator, use an object of class type as the "name" of the function. Pass the arguments the way you would pass any other function arguments. With a simple variable of class type, the syntax looks like an ordinary function call.

An object that overloads the function call operator is often called a functor. Functors are often used with the standard algorithms as a way of preserving state between function calls or just to better encapsulate functionality. See <algorithm> and <functional> in Chapter 13 for examples.

New and Delete

You can overload the global new and delete operators, or you can overload the operators of a particular class. If you do not overload the global operators, the C++ library provides an implementation for you. (See <new> in Chapter 13.) If you do not overload the operators for a class, the global operators are used.

If a class overloads the new and delete operators, the operator functions are called for new and delete expressions involving that class. When overloading a class operator new or delete, you can call the global operator, as shown in Example 6-24.

Example 6-24: Overloading new and delete.

#include <iostream>
#include <memory>
#include <new>
#include <ostream>

class singleton
{
public:
  void* operator new(size_t size) throw (std::bad_alloc) {
    std::cout << "singleton::new\n";
    if (instance == 0)
      instance = ::new singleton;
    ++count;
    return instance;
  }
  void operator delete(void* p) {
    std::cout << "singleton::delete\n";
    if (--count == 0) {
      ::delete instance;
      instance = 0;
    }
  }

  static singleton* make() { return new singleton(); }

private:
  singleton() {}
  singleton(const singleton&);
  static singleton* instance;
  static size_t count;
};

singleton* singleton::instance;
size_t singleton::count;

int main()
{
  std::auto_ptr<singleton> s1(singleton::make());
  std::auto_ptr<singleton> s2(singleton::make());
  return s1.get() == s2.get();
}

If you overload new, you should probably overload delete, too.

Type Conversion

A class can declare type conversion operators to convert objects of the class type to other types. The operator functions must be non-static member functions. The name of the operator is the desired type, which can be a series of type specifiers and pointer operators, but cannot be a function or array type. For example,

class bigint {
public:
  operator long(); // convert object to type long
  operator unsigned long();
  operator const char*(); // return a string representation
  ...
};

The main Function

Every program has a function in the global namespace called main, which is the main program. This function must return type int. The C++ environment calls main; your program must never call main. The main function cannot be declared inline or static. It can be called with no arguments or with two arguments, as follows. (An implementation can permit other parameters.)

int main(int argc, char* argv[])

Where argc is the number of command line arguments, and argv is an array of pointers to the command line arguments as null-terminated character strings. By definition, argv[argc] is a null pointer. The first element of the array (argv[0]) is the program name or an empty string.

Static objects at namespace scope can have constant or dynamic initial values. Static objects with constant values are initialized by constant data before the program starts. Static objects with dynamic values are initialized by code when the program begins. When, exactly, the objects are initialized is implementation-defined. It might happen before main is called, or it might be after.

You should try to avoid writing code that depends on the order in which static objects are initialized. If you cannot avoid it, you can work around the problem by defining an initialization class. For example, you can guarantee that the standard I/O stream objects are created early, so they can be used in the constructor of a static object. See the <ios> in Chapter 13 for more information and an example.

Local static objects are initialized when their functions are called. The object is guaranteed to be initialized before the function body starts. If the function is never called, the object is never initialized.

When main returns or when exit is called (see <cstdlib> in Chapter 13), static objects are destroyed in the reverse order of their construction, and the program terminates. All local, static objects are also destroyed. If a function that contains a local static object is called during the destruction of static objects, the behavior is undefined.

The value returned from main is passed to the host environment. You can return zero or EXIT_SUCCESS (declared in <cstdlib>) to indicate success, or EXIT_FAILURE to tell the environment that the program failed. Other values are implementation-defined. Some environments ignore the value returned from main; others rely on the value.