13 Overloading [over]

13.1 Overloadable declarations [over.load]

Not all function declarations can be overloaded. Those that cannot be overloaded are specified here. A program is ill-formed if it contains two such non-overloadable declarations in the same scope. [ Note: This restriction applies to explicit declarations in a scope, and between such declarations and declarations made through a using-declaration ([namespace.udecl]). It does not apply to sets of functions fabricated as a result of name lookup (e.g., because of using-directives) or overload resolution (e.g., for operator functions).  β€” end note ]

Certain function declarations cannot be overloaded:

  • Function declarations that differ only in the return type cannot be overloaded.

  • Member function declarations with the same name and the same parameter-type-list cannot be overloaded if any of them is a static member function declaration ([class.static]). Likewise, member function template declarations with the same name, the same parameter-type-list, and the same template parameter lists cannot be overloaded if any of them is a static member function template declaration. The types of the implicit object parameters constructed for the member functions for the purpose of overload resolution ([over.match.funcs]) are not considered when comparing parameter-type-lists for enforcement of this rule. In contrast, if there is no static member function declaration among a set of member function declarations with the same name and the same parameter-type-list, then these member function declarations can be overloaded if they differ in the type of their implicit object parameter. [ Example: the following illustrates this distinction:

    class X {
      static void f();
      void f();                     // ill-formed
      void f() const;               // ill-formed
      void f() const volatile;      // ill-formed
      void g();
      void g() const;               // OK: no static g
      void g() const volatile;      // OK: no static g
    };
    

     β€” end example ]

  • Member function declarations with the same name and the same parameter-type-list as well as member function template declarations with the same name, the same parameter-type-list, and the same template parameter lists cannot be overloaded if any of them, but not all, have a ref-qualifier ([dcl.fct]). [ Example:

    class Y {
      void h() &;
      void h() const &;             // OK
      void h() &&;                  // OK, all declarations have a ref-qualifier
      void i() &;
      void i() const;               // ill-formed, prior declaration of i
                                    // has a ref-qualifier
    };
    

     β€” end example ]

Note: As specified in [dcl.fct], function declarations that have equivalent parameter declarations declare the same function and therefore cannot be overloaded:

  • Parameter declarations that differ only in the use of equivalent typedef β€œtypes” are equivalent. A typedef is not a separate type, but only a synonym for another type ([dcl.typedef]). [ Example:

    typedef int Int;
    
    void f(int i);
    void f(Int i);                  // OK: redeclaration of f(int)
    void f(int i) { /* ... */ }
    void f(Int i) { /* ... */ }    // error: redefinition of f(int)
    
    

     β€” end example ]

    Enumerations, on the other hand, are distinct types and can be used to distinguish overloaded function declarations. [ Example:

    enum E { a };
    
    void f(int i) { /* ... */ }
    void f(E i)   { /* ... */ }
    

     β€” end example ]

  • Parameter declarations that differ only in a pointer * versus an array [] are equivalent. That is, the array declaration is adjusted to become a pointer declaration ([dcl.fct]). Only the second and subsequent array dimensions are significant in parameter types ([dcl.array]). [ Example:

    int f(char*);
    int f(char[]);                  // same as f(char*);
    int f(char[7]);                 // same as f(char*);
    int f(char[9]);                 // same as f(char*);
    
    int g(char(*)[10]);
    int g(char[5][10]);             // same as g(char(*)[10]);
    int g(char[7][10]);             // same as g(char(*)[10]);
    int g(char(*)[20]);             // different from g(char(*)[10]);
    

     β€” end example ]

  • Parameter declarations that differ only in that one is a function type and the other is a pointer to the same function type are equivalent. That is, the function type is adjusted to become a pointer to function type ([dcl.fct]). [ Example:

    void h(int());
    void h(int (*)());              // redeclaration of h(int())
    void h(int x()) { }             // definition of h(int())
    void h(int (*x)()) { }          // ill-formed: redefinition of h(int())
    

     β€” end example ]

  • Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when determining which function is being declared, defined, or called. [ Example:

    typedef const int cInt;
    
    int f (int);
    int f (const int);              // redeclaration of f(int)
    int f (int) { /* ... */ }      // definition of f(int)
    int f (cInt) { /* ... */ }     // error: redefinition of f(int)
    

     β€” end example ]

    Only the const and volatile type-specifiers at the outermost level of the parameter type specification are ignored in this fashion; const and volatile type-specifiers buried within a parameter type specification are significant and can be used to distinguish overloaded function declarations.124 In particular, for any type T, β€œpointer to T,” β€œpointer to const T,” and β€œpointer to volatile T” are considered distinct parameter types, as are β€œreference to T,” β€œreference to const T,” and β€œreference to volatile T.”

  • Two parameter declarations that differ only in their default arguments are equivalent. [ Example: consider the following:

    void f (int i, int j);
    void f (int i, int j = 99);     // OK: redeclaration of f(int, int)
    void f (int i = 88, int j);     // OK: redeclaration of f(int, int)
    void f ();                      // OK: overloaded declaration of f
    
    void prog () {
        f (1, 2);                   // OK: call f(int, int)
        f (1);                      // OK: call f(int, int)
        f ();                       // Error: f(int, int) or f()?
    }
    

     β€” end example ]  β€” end note ]

When a parameter type includes a function type, such as in the case of a parameter type that is a pointer to function, the const and volatile type-specifiers at the outermost level of the parameter type specifications for the inner function type are also ignored.