Appendix C

Overload Resolution

Overload resolution is the process that selects the function to call for a given call expression. Consider the following simple example:

void display_num(int);    // #1
void display_num(double); // #2

int main()
{
    display_num(399);    // #1 matches better than #2
    display_num(3.99);   // #2 matches better than #1
}

In this example, the function name display_num() is said to be overloaded. When this name is used in a call, a C++ compiler must therefore distinguish between the various candidates using additional information; mostly, this information is the types of the call arguments. In our example, it makes intuitive sense to call the int version when the function is called with an integer argument and the double version when a floating-point argument is provided. The formal process that attempts to model this intuitive choice is the overload resolution process.

The general ideas behind the rules that guide overload resolution are simple enough, but the details have become quite complex during the C++ standardization process. This complexity was driven mostly by the desire to support various real-world examples that intuitively (to a human) seem to have an “obviously best match,” but when trying to formalize this intuition, various subtleties arose.

In this appendix, we provide a reasonably detailed survey of the overload resolution rules. However, the complexity of this process is such that we do not claim to cover every part of the topic.

C.1 When Does Overload Resolution Kick In?

Overload resolution is just one part of the complete processing of a function call. In fact, it is not part of every function call. First, calls through function pointers and calls through pointers to member functions are not subject to overload resolution because the function to call is entirely determined (at run time) by the pointers. Second, function-like macros cannot be overloaded and are therefore not subject to overload resolution.

At a very high level, a call to a named function can be processed in the following way:

• The name is looked up to form an initial overload set.

• If necessary, this set is adjusted in various ways (e.g., template argument deduction and substitution occurs, which can cause some function template candidates to be discarded).

• Any candidate that doesn’t match the call at all (even after considering implicit conversions and default arguments) is eliminated from the overload set. This results in a set of viable function candidates.

• Overload resolution is performed to find a best candidate. If there is one, it is selected; otherwise, the call is ambiguous.

• The selected candidate is checked. For example, if it is a deleted function (i.e., one defined with = delete) or an inaccessible private member function, a diagnostic is issued.

Each of these steps has its own subtleties, but overload resolution is arguably the most complex. Fortunately, a few simple principles clarify the majority of situations. We examine these principles next.

C.2 Simplified Overload Resolution

Overload resolution ranks the viable candidate functions by comparing how each argument of the call matches the corresponding parameter of the candidates. For one candidate to be considered better than another, the better candidate cannot have any of its parameters be a worse match than the corresponding parameter in the other candidate. The following example illustrates this:

void combine(int, double);
void combine(long, int);

int main()
{
    combine(1, 2);        // ambiguous!
}

In this example, the call to combine() is ambiguous because the first candidate matches the first argument (the literal 1 of type int) best, whereas the second candidate matches the second argument best. We could argue that int is in some sense closer to long than to double (which supports choosing the second candidate), but C++ does not attempt to define a measure of closeness that involves multiple call arguments.

Given this first principle, we are left with specifying how well a given argument matches the corresponding parameter of a viable candidate. As a first approximation, we can rank the possible matches as follows (from best to worst):

1. Perfect match. The parameter has the type of the expression, or it has a type that is a reference to the type of the expression (possibly with added const and/or volatile qualifiers).

2. Match with minor adjustments. This includes, for example, the decay of an array variable to a pointer to its first element or the addition of const to match an argument of type int** to a parameter of type int const* const*.

3. Match with promotion. Promotion is a kind of implicit conversion that includes the conversion of small integral types (such as bool, char, short, and sometimes enumerations) to int, unsigned int, long, or unsigned long, and the conversion of float to double.

4. Match with standard conversions only. This includes any sort of standard conversion (such as int to float) or conversion from a derived class to one of its public, unambiguous base classes but excludes the implicit call to a conversion operator or a converting constructor.

5. Match with user-defined conversions. This allows any kind of implicit conversion.

6. Match with ellipsis (). An ellipsis parameter can match almost any type. However, there is one exception: Class types with a nontrivial copy constructor may or may not be valid (implementations are free to allow or disallow this).

The following contrived example illustrates some of these matches:

int f1(int);     // #1
int f1(double);  // #2
f1(4);           // calls #1 : perfect match (#2 requires a standard conversion)

int f2(int);     // #3
int f2(char);    // #4
f2(true);        // calls #3 : match with promotion
                 //           (
#4 requires stronger standard conversion)

class X {
  public:
    X(int);
};
int f3(X);
      // #5
int f3(…);     // #6
f3(7);           // calls #5 : match with user-defined conversion
                 //           (#6 requires a match with ellipsis)

Note that overload resolution occurs after template argument deduction, and this deduction does not consider all these sorts of conversions. For example:

template<typename T>
class MyString {
  public:
    MyString(T const*);   // converting constructor
    …
};

template<typename T>
MyString<T> truncate(MyString<T> const&, int);
int main()
{
    MyString<char> str1, str2;
    str1 = truncate<char>("Hello World", 5);  //OK
    str2 = truncate("Hello World", 5);        //ERROR
}

The implicit conversion provided through the converting constructor is not considered during template argument deduction. The assignment to str2 finds no viable function truncate(); hence overload resolution is not performed at all.

In the context of template argument deduction, recall also that an rvalue reference to a template parameter can deduce to either an lvalue reference type (after reference collapsing) if the corresponding argument is an lvalue or to an rvalue reference type if that argument is an rvalue (see Section 15.6 on page 277). For example:

template<typename T> void strange(T&&, T&&);
template<typename T> void bizarre(T&&, double&&);

int main()
{
    strange(1.2, 3.4);        // OK: with T deduced to double
    double val = 1.2;
    strange(val, val);        // OK: with T deduced to double&
    strange(val, 3.4);        // ERROR: conflicting deductions
    bizarre(val, val);        // ERROR: lvalue val doesn’t match double&&
}

The previous principles are only a first approximation, but they cover many cases. Yet there are quite a few common situations that are not adequately explained by these rules. We proceed with a brief discussion of the most important refinements of these rules.

C.2.1 The Implied Argument for Member Functions

Calls to nonstatic member functions have a hidden parameter that is accessible in the definition of the member function as *this. For a member function of a class MyClass, the hidden parameter is usually of type MyClass& (for non-const member functions) or MyClass const& (for const member functions).1 This is somewhat surprising given that this has a pointer type. It would have been nicer to make this equivalent to what is now *this. However, this was part of an early version of C++ before reference types were part of the language, and by the time reference types were added, too much code already depended on this being a pointer.

The hidden *this parameter participates in overload resolution just like the explicit parameters. Most of the time this is quite natural, but occasionally it comes unexpectedly. The following example shows a string-like class that does not work as intended (yet we have seen such code in the real world):

#include <cstddef>

class BadString {
  public:
    BadString(char const*);
    …

    // character access through subscripting:
    char& operator[] (std::size_t);                          // #1
    char const& operator[] (std::size_t) const;

    // implicit conversion to null-terminated byte string:
    operator char* ();                                       // #2
    operator char const* ();
    …
};

int main()
{
    BadString str("correkt");
    str[5] = ’c’//possibly an overload resolution ambiguity!
}

At first, nothing seems ambiguous about the expression str[5]. The subscript operator at #1 seems like a perfect match. However, it is not quite perfect because the argument 5 has type int, and the operator expects an unsigned integer type (size_t and std::size_t usually have type unsigned int or unsigned long, but never type int). Still, a simple standard integer conversion makes #1 easily viable. However, there is another viable candidate: the built-in subscript operator. Indeed, if we apply the implicit conversion operator to str (which is the implicit member function argument), we obtain a pointer type, and now the built-in subscript operator applies. This built-in operator takes an argument of type ptrdiff_t, which on many platforms is equivalent to int and therefore is a perfect match for the argument 5. So even though the built-in subscript operator is a poor match (by user-defined conversion) for the implied argument, it is a better match than the operator defined at #1 for the actual subscript! Hence the potential ambiguity.2 To solve this kind of problem portably, you can declare operator [ ] with a ptrdiff_t parameter, or you can replace the implicit type conversion to char* by an explicit conversion (which is usually recommended anyway).

It is possible for a set of viable candidates to contain both static and nonstatic members. When comparing a static member with a nonstatic member, the quality of the match of the implicit argument is ignored (only the nonstatic member has an implicit *this parameter).

By default, a nonstatic member function has an implicit *this parameter that is an lvalue reference type, but C++11 introduced syntax to make it an rvalue reference type. For example:

struct S {
    void f1();        // implicit *this parameter is an lvalue reference (see below)
    void f2() &&;     // implicit *this parameter is an rvalue reference
    void f3() &;      // implicit *this parameter is an lvalue reference
};

As you can tell from this example, it is possible not only to make the implicit parameter an rvalue reference (with the && suffix) but also to affirm the lvalue reference case (with the & suffix). Interestingly, specifying the & suffix is not exactly equivalent to leaving it off: An old special-case permits an rvalue to be bound to an lvalue reference to non-const type when that reference is the traditional implicit *this parameter, but that (somewhat dangerous) special case no longer applies if the lvalue reference treatment was requested explicitly. So, with the definition of S specified above:

int main()
{
    S().f1();   // OK: old rule allows rvalue S() to match implied
                //     lvalue reference type S& of *this
    S().f2();   // OK: rvalue S() matches rvalue reference type
                //     of *this
    S().f3();   // ERROR: rvalue S() cannot match explicit lvalue
                //        reference type of *this
}

C.2.2 Refining the Perfect Match

For an argument of type X, there are four common parameter types that constitute a perfect match: X, X&, X const&, and  X&& (X const&& is also an exact match, but it is rarely used). However, it is rather common to overload a function on two kinds of references. Prior to C++11, this meant cases like these:

void report(int&);        // #1
void report(int const&);  // #2

int main()
{
    for (int k = 0; k<10; ++k) {
         report(k);      // calls #1
    }
    report(42);          // calls #2
}

Here, the version without the extra const is preferred for lvalues, whereas only the version with const can match rvalues.

With the addition of rvalue references in C++11, another common case of two perfect matches needing to be distinguished is illustrated by the following example:

struct Value {
  …
};
void pass(Value const&);
  // #1
void pass(Value&&);       // #2

void g(X&& x)
{
  pass(x);                // calls #1 , because x is an lvalue
  pass(X());              // calls #2 , because X() is an rvalue (in fact, prvalue)
  pass(std::move(x));     // calls #2 , because std::move(x) is an rvalue (in fact, xvalue)
}

This time, the version taking an rvalue reference is considered a better match for rvalues, but it cannot match lvalues.

Note that this also applies to the implicit argument of a member function call:

class Wonder {
  public:
    void tick();              // #1
    void tick() const;        // #2
    void tack() const;        // #3
};

void run(Wonder& device)
{
  device.tick();              // calls #1
  device.tack();              // calls #3 , because there is no non-const version
                              //            of Wonder::tack()
}

Finally, the following modification of our earlier example illustrates that two perfect matches can also create an ambiguity if you overload with and without references:

void report(int);         // #1
void report(int&);        // #2
void report(int const&);  // #3

int main()
{
    for (int k = 0; k<10; ++k) {
        report(k);        // ambiguous: #1 and #2 match equally well
    }
    report(42);           // ambiguous: #1 and #3 match equally well
}

C.3 Overloading Details

The previous section covers most of the overloading situations encountered in everyday C++ programming. There are, unfortunately, many more rules and exceptions to these rules—more than is reasonable to present in a book that is not really about function overloading in C++. Nonetheless, we discuss some of them here in part because they apply somewhat more often than other rules and in part to provide a sense for how deep the details go.

C.3.1 Prefer Nontemplates or More Specialized Templates

When all other aspects of overload resolution are equal, a nontemplate function is preferred over an instance of a template (it doesn’t matter whether that instance is generated from the generic template definition or whether it is provided as an explicit specialization). For example:

template<typename T> int f(T);    // #1
void f(int);                      // #2

int main()
{
    return f(7);     // ERROR: selects #2 , which doesn’t return a value
}

This example also clearly illustrates that overload resolution normally does not involve the return type of the selected function.

However, when other aspects of overload resolution slightly differ (such as having different const and reference qualifiers), first the general rules of overload resolution apply. This effect can easily accidentally cause surprising behavior, when member functions are defined that accept the same arguments as copy or move constructors. See Section 16.2.4 on page 333 for details.

If the choice is between two templates, then the most specialized of the templates is preferred (provided one is actually more specialized than the other). See Section 16.2.2 on page 330 for a thorough explanation of this concept. One special case of this distinction occurs when two templates only differ in that one adds a trailing parameter packs: The template without the pack is considered more specialized and is therefore preferred if it matches the call. Section 4.1.2 on page 57 discusses an example of this situation.

C.3.2 Conversion Sequences

An implicit conversion can, in general, be a sequence of elementary conversions. Consider the following code example:

class Base {
  public:
    operator short() const;
};

class Derived : public Base {
};

void count(int);

void process(Derived const& object)
{
    count(object);   // matches with user-defined conversion
}

The call count(object) works because object can implicitly be converted to int. However, this conversion requires several steps:

1. A conversion of object from Derived const to Base const (this is a glvalue conversion; it preserves the identity of the object)

2. A user-defined conversion of the resulting Base const object to type short 3. A promotion of short to int

This is the most general kind of conversion sequence: a standard conversion (a derived-to-base conversion, in this case), followed by a user-defined conversion, followed by another standard conversion. Although there can be at most one user-defined conversion in a conversion sequence, it is also possible to have only standard conversions.

An important principle of overload resolution is that a conversion sequence that is a subsequence of another conversion sequence is preferable over the latter sequence. If there were an additional candidate function

void count(short);

in the example, it would be preferred for the call count(object) because it doesn’t require the third step (promotion) in the conversion sequence.

C.3.3 Pointer Conversions

Pointers and pointers to members undergo various special standard conversions, including

• Conversions to type bool

• Conversions from an arbitrary pointer type to void*

• Derived-to-base conversions for pointers

• Base-to-derived conversions for pointers to members

Although all of these can cause a “match with standard conversions only,” they are not ranked equally.

First, conversions to type bool (both from a regular pointer and from a pointer to a member) are considered worse than any other kind of standard conversion. For example:

;void check(void*);  // #1
void check(bool);    // #2

void rearrange (Matrix* m)
{
    check(m);        // calls #1
    …
}

Within the category of regular pointer conversions, a conversion to type void* is considered worse than a conversion from a derived class pointer to a base class pointer. Furthermore, if conversions to different classes related by inheritance exist, a conversion to the most derived class is preferred. Here is another short example:

class Interface {
    …
};

class CommonProcesses : public Interface {
    …
};

class Machine : public CommonProcesses {
    …
};

char* serialize(Interface*);            // #1
char* serialize(CommonProcesses*);      // #2

void dump (Machine* machine)
{
    char* buffer = serialize(machine);  // calls #2
    …
}

The conversion from Machine* to CommonProcesses* is preferred over the conversion to Interface*, which is fairly intuitive.

A very similar rule applies to pointers to members: Between two conversions of related pointer-to-member types, the “closest base” in the inheritance graph (i.e., the least derived) is preferred.

C.3.4 Initializer Lists

Initializer list arguments (initializers passed with in curly braces) can be converted to several different kinds of parameters: initializer_lists, class types with an initializer_list constructor, class types for which the initializer list elements can be treated as (separate) parameters to a constructor, or aggregate class types whose members can be initialized by the elements of the initializer list. The following program illustrates these cases:

overload/initlist.cpp

#include <initializer_list>
#include <string>
#include <vector>
#include <complex>
#include <iostream>

void f(std::initializer_list<int>) {
  std::cout << "#1\n";
}

void f(std::initializer_list<std::string>) {
  std::cout << "#2\n";
}

void g(std::vector<int> const& vec) {
  std::cout << "#3\n";
}
void h(std::complex<double> const& cmplx) {
  std::cout << "#4\n";
}

struct Point {
  int x, y;
};
void i(Point const& pt) {
  std::cout << "#5\n";
}
int main()
{
  f({1, 2, 3});                         // prints #1
  f({"hello", "initializer", "list"});  // prints #2
  g({1, 1, 2, 3, 5});                   // prints #3
  h({1.5, 2.5});                        // prints #4
  i({1, 2});                            // prints #5
}

In the first two calls to f(), the initializer list arguments are converted to std::initializer_list values, which involves converting each of the elements in the initializer list to the element type of the std::initializer_list. In the first call, all of the elements are already of type int, so no additional conversion is needed. In the second call, each string literal in the initializer list is converted to a std::string by calling the string(char const*) constructor. The third call (to g()) performs a user-defined conversion using the std::vector(std::initializer_list<int>) constructor. The next call invokes the std::complex(double, double) constructor, as if one had written std::complex<double>(1.5, 2.5). The final call performs aggregate initialization, which initializes the members of an instance of the Point class from the elements in the initializer list without calling a constructor of Point.3

There are several interesting overloading cases for initializer lists. When converting an initializer list to an initializer_list, as in the first two calls of the example above, the overall conversion is given the same ranking as the worst conversion from any given element in the initializer list to the element type of the initializer_list (i.e., the T in initializer_list<T>). This can lead to some surprises, as in the following example:

overload/initlistovl.cpp

#include <initializer_list>
#include <iostream>

void ovl(std::initializer_list<char>) {   //#1
  std::cout << "#1\n";
}

void ovl(std::initializer_list<int>) {    //#2
  std::cout << "#2\n";
}

int main()
{
  ovl({’h’, ’e’, ’l’, ’l’, ’o’, ’\0’});   //prints #1
  o
vl({’h’, ’e’, ’l’, ’l’, ’o’, 0});      //prints #2
}

In the first call to ovl(), each element of the initializer list is a char. For the first ovl() function, these elements require no conversion at all. For the second ovl() function, these elements require a promotion to int. Because the perfect match is better than a promotion, the first call to ovl() calls #1 .

In the second call to ovl(), the first five elements are of type char, while the last is of type int. For the first ovl() function, the char elements are a perfect match, but the int requires a standard conversion, so the overall conversion is ranked as a standard conversion. For the second ovl() function, the char elements require a promotion to int, while the int element at the end is a perfect match. The overall conversion for the second ovl() function is ranked as a promotion, which makes it a better candidate than the first ovl(), even though only a single element’s conversion was better.

When initializing an object of class type with an initializer list, as in the calls to g() and h() in our original example, overload resolution proceeds in two phases:

1. The first phase considers only initializer-list constructors, that is, constructors whose only nonde-faulted parameter is of type std::initializer_list<T> for some type T (after removing the top-level reference and const/volatile qualifiers).

2. If no such viable constructor is found, then the second phase considers all other constructors. There is one exception to this rule: If the initializer list is empty and the class has a default constructor, the first phase is skipped so that the default constructor will be called.

The effect of this rule is that any initializer-list constructor is a better match than any non-initializer-list constructor, as illustrated in the following example:

overload/initlistctor.cpp

#include <initializer_list>
#include <string>
#include <iostream>

template<typename T>
struct Array {
  Array(std::initializer_list<T>) {
    std::cout << "#1\n";
}
  Array(unsigned n, T const&) {
    std::cout << "#2\n
";
  }
};

void arr1(Array<int>) {
}

void arr2(Array<std::string>) {
}

int main()
{
  arr1({1, 2, 3, 4, 5});                   // prints #1
  arr1({1, 2});                            // prints #1
  arr1({10u, 5});                          // prints #1
  arr2({"hello"
, "initializer", "list"});  //prints #1
  arr2({10, "hello"
});                     //prints #2
}

Note that the second constructor, which takes an unsigned and a T const&, won’t be called when initializing an Array<int> object from an initializer list, because its initializer-list constructor is always a better match than its non-initializer-list constructors. With Array<string>, however, the non-initializer-list constructor will be called when the initializer-list constructor is not viable, as in the second call to arr2().

C.3.5 Functors and Surrogate Functions

We mentioned earlier that after the name of a function has been looked up to create an initial overload set, the set is tweaked in various ways. An interesting situation arises when a call expression refers to a class type object instead of a function. In this case, there are two potential additions to the overload set.

The first addition is straightforward: Any member operator () (the function call operator) is added to the set. Objects with such operators are usually called functors or function objects (see Section 11.1 on page 157).

A less obvious addition occurs when a class type object contains an implicit conversion operator to a pointer to a function type (or to a reference to a function type).4 In such situations, a dummy (or surrogate) function is added to the overload set. This surrogate function candidate is considered to have an implied parameter of the type designated by the conversion function, in addition to parameters with types corresponding to the parameter types in the destination type of that conversion function. An example makes this much clearer:

using FuncType = void (double, int);

class IndirectFunctor {
  public:
    …
    void operator()(double, double) const;
    operator FuncType*() const;
};

void activate(IndirectFunctor const& funcObj)
{
    funcObj(3, 5);  // ERROR: ambiguous
}

The call funcObj(3, 5) is treated as a call with three arguments: funcObj, 3, and 5. The viable function candidates include the member operator() (which is treated as having parameter types IndirectFunctor const&, double, and double) and a surrogate function with parameters of type FuncType*, double, and int. The surrogate function has a worse match for the implied parameter (because it requires a user-defined conversion), but it has a better match for the last parameter; hence the two candidates cannot be ordered. The call is therefore ambiguous.

Surrogate functions are in the most obscure corners of C++ and rarely occur in practice (fortunately).

C.3.6 Other Overloading Contexts

So far we have discussed overloading in the context of determining which function should be called in a call expression. However, there are a few other contexts in which a similar selection must be made.

The first context occurs when the address of a function is needed. Consider the following example:

int numElems(Matrix const&);     // #1
int numElems(Vector const&);     // #2

int (*funcPtr)(Vector const&) = numElems;  // selects #2

Here, the name numElems refers to an overload set, but only the address of one function in that set is desirable. Overload resolution then attempts to match the required function type (the type of funcPtr in this example) to the available candidates.

The other context that requires overload resolution is initialization. Unfortunately, this is a topic fraught with subtleties that are beyond what can be covered in an appendix. However, a simple example at least illustrates this additional aspect of overload resolution:

#include <string>

class BigNum {
  public:
    BigNum(long n);        // #1
    BigNum(double n);        // #2
    BigNum(std::string const&);        // #3
    …
    operator double();        // #4
    operator long();        // #5
    …
};
void initDemo()
{
    BigNum bn1(100103);        // selects #1
    BigNum bn2("7057103224.095764");        //selects #3
    int in = bn1;        // selects #5
}

In this example, overload resolution is needed to select the appropriate constructor or conversion operator. Specifically, the initialization of bn1 calls the first constructor, that of bn2 calls the third constructor, and that of in() calls operator long(). In the vast majority of cases, the overloading rules produce the intuitive result. However, the details of these rules are quite complex, and some applications rely on some of the more obscure corners in this area of the C++ language.

1 It could also be of type MyClass volatile& or MyClass const volatile& if the member function was volatile, but this is extremely rare.

2 Note that the ambiguity exists only on platforms for which size_t is a synonym for unsigned int. On platforms for which it is a synonym for unsigned long, the type ptrdiff_t is a type alias of long, and no ambiguity exists because the built-in subscript operator also requires a conversion of the subscript expression.

3 Aggregate initialization is only available for aggregate types in C++, which are either arrays or simple, C-like classes that have no user-provided constructors, no private or protected nonstatic data members, no base classes, and no virtual functions. Prior to C++14, they must also not have a default member initializer. Since C++17, public base classes are allowed.

4 The conversion operator must also be applicable in the sense that, for example, a non-const operator is not considered for const objects.