This is the mail archive of the mailing list for the Archer project.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ Overload Resolution

This is a summary of the research I have been doing on the subject. This is probably going to be a long email, but I am doing it mainly as an exercise to ensure that I record all I understood from the spec and understand gdb to do.

This is a summary of Chapter 4, section 13.3, and section of the C++ spec and some googling, guessing and compiling.

I would appreciate your input, questions, and corrections.


Overload resolution takes place when:

1) invoking a function by name:
  - ap->foo()
  - foo()

2) An invocation of the function call operator '()'
  - class A{
      int operator (int) {}
      int operator (char){}
    A a;

3) Invoking a pointer-to-function conversion function.
  I don't know what that is. I guessed this:
  class A {
    int (*foop)();
    operator int (*)() () { return foop; }
  but that does not compile

4) Invoking a reference-to-pointer-to-function conversion function
  Reaction similar to the above.

5) Invoking a reference-to-function conversion function

6) Invoking an operator referenced in an expression
  Class A{
    int operator==(int);
    int operator==(char);
  a == 1;

7) Invocation of a constructor for direct initialization of a class
  class A{
  A a(1);

8) Invoking a user-defined conversion for copy-initialization of a class
  class A {};
  class B {
    operator A ();
    operator X ();
  B b;
  A a = b;

9) Invoking a conversion function for initialization of an object of a
  nonclass type from an expression of class type
  class A {
    operator int ();
    operator char ();
  A a;
  int x = a;

10) invoking a conversion function for conversion to an lvalue to which
  a reference will be directly bound.
  I guessed this:
    class A {};
  class B {
    operator A ();
  B b;
  A &a = b;

AFAIK Only 1,2 and 6 are supported by gdb.

Viable Functions ================

From the set of candidate functions viable functions are selected
for a function to be viable it must:
- Match the number of parameters in the function call
 - exact match
 - exrta parameters can be matched by an ellipsis in a candidate
 - candidates with default value parameters can match function calls
   with to few arguments.
- Have a conversion sequence which converts each argument to its
  corresponding parameter. (more about conversion sequences later)

Best Viable Function ====================

- function f1 is better than f2 if for every argument i f1 has a
  conversion squence that is no worse than that of f2 and:
  - for some argument j f1 has a better conversion sequence than f2. Or,
  - F1 is a non-template function and F2 is a template instance.
  - F1 is a more specialized template instance
    template<class T> void f(T)        // #1
    template<class T> void f(T*)       // #2
    template<class T> void f(const T*) // #3
    const int *p;
    Here #3 is more specialized than #2 which is more specialized
    than #1
  - The context is a user defined conversion and F1 has a better
    conversion sequence from the return type than F2
    struct A {
      operator int ();
      operator double ();
    A a;
    int i = a;
    Here 'operator int ()' has a better conversion sequence from the
    return type than 'operator double ()'

If we have more than one equally viable functions then the expression is incorrect. This is not handled by gdb.

Conversion Sequences

They come in 3 flavors:

- standard
  A standard conversion is a conversion that converts one built in type
  to another built in type.
  Here are the standard Conversions:

[This is some example code to be used in the following table

  int foo1 (int)  {return 0;}
  int foo2 (int*) {return 0;}
  int foo3 (int (*)(int)) {return 0;}
  int foo4 (const volatile int*) {return 0;}
  int foo5 (double) {return 0;}
  int foo6 (float) {return 0;}

  int a = 3;
  int b[3] = {1,2,3};
  volatile int *c;
  char d;
  float e;
  long f = 1;
  double g = 1.0;

  class H {};
  class I:public H {};
  I i;
  int foo7 (H*) {return 0;}

  void (H::*j)();
  int foo8 (void (I::*)()) {return 0;}

int foo9 (bool) {return 0;}


 | *Conversion*                     | *Rank*      | *Example*   |
 | No conversions required          | Exact Match | foo1 (3)    |
 | Lvalue-to-rvalue                 | Exact Match | foo1 (a)    |
 | Array-to-pointer                 | Exact Match | foo2 (b)    |
 | Function-to-pointer              | Exact Match | foo3 (foo1) |
 | Qualification                    | Exact Match | foo4 (c)    |
 | Integral Promotions              | Promotion   | foo1 (d)    |
 | Float Promotions                 | Promotion   | foo5 (e)    |
 | Integral Conversions             | Conversions | foo1 (f)    |
 | Float Conversions                | Conversions | foo6 (g)    |
 | Float-integral Conversions       | Conversions | foo6 (a)    |
 | Pointer Conversions              | Conversions | foo7 (&i)   |
 | Pointer to member Conversions    | Conversions | foo8 (j)    |
 | Boolean Conversions              | Conversions | foo9 (j)    |

For more detail and for the exact definitions of these conversions please refert ot Chapter 4 "Standard Conversions" of the C++ spec

Exact matches are preferred to promotions which are preferred to Conversions. Any conversions which have the same rank lead to an ambiguous resolution. More on ranking later.

* Reference binding is not considered a conversion when a reference parameter is binding a argument of the same type. Otherwise it is whatever conversion is required to convert the argument type the reference type.

- user defined
  A user defined conversion sequence has three parts:
  1. A standard conversion converting the arguments to the types of the
     parameters of the user defined conversion function.
  2. A conversion performed by the user defined conversion function
     Ex: class A { operator int () { return this->someInt; }};
     that is a user defined conversion of A to an int.
  3. Conversion of the return value of the conversion function to the
     final desired type.

- ellipsis
  An ellipsis conversion matches A type to the ellipses parameter
  specified by the function called.
  void foo(...) {}
  foo (3)


1. A standard conversion sequence is better than a user-defined
   conversion sequence which is better than an ellipsis conversion

2. Standard conversion sequence S1 is better than standard conversion
   sequence S2 if:
   - S1 is a subsequence of S2
   - S1 has a better 'rank' than S2
   - S1 and S2 are both 'qualification conversions' but
     cv-qualification of S1 is a subset of cv-qualification of S2
     This goes for both pointers and references.
   - S1 and S2 have the same rank but:
     - S2 is a conversion of a pointer, or pointer to memeber to bool
       and S1 is not.
     - Class B is derived from class A, and S1 is a conversion of B* to
       A* and S2 is a conversion of B* to void*.
     - Class C is derived from B which is derived from A, and S1 is a
       conversion from C* to B* vs S2, a conversion from C* to A*.
     - Same as above but for reference binding.
     - S1 is a conversion from A::* to B::* and S2 is A::* to C::* and
       vice versa.
     - S1 is a conversion from C to B and S2 is C to A and vice versa
     - S1 is a conversion from B* to A* and S2 is C* to A*
     - Same as above but for references.

3. User defined conversion U1 is better than user defined Conversion U2
   if U1 and U2 are using the same conversion function and U1 has a
   better second standard conversion sequence than U2.
     struct A { operator short();}
     int f(int);
     int f (float);
     int i = f(a) // this will call f(int)

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]