This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 114a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-04-18


2701. Default arguments in multiple scopes / inheritance of array bounds in the same scope

Section: 9.3.4.7  [dcl.fct.default]     Status: open     Submitter: Richard Smith     Date: 2020-11-20

It appears that P1787R6 has inadvertently changed the rules for where default arguments can be (re-)declared for a parameter. Consider:

namespace N {
  void f(int a, int b = 1);
}
void N::f(int a = 1, int b) {} // valid before P1787R6, invalid afterwards
void N::f(int a, int b = 2) {} // invalid before P1787R6, valid afterwards,
  // but this default argument will either never be used or will make any call using it ill-formed (unclear which)

Subclause 9.3.4.7 [dcl.fct.default] paragraph 4 specifies:

For non-template functions, default arguments can be added in later declarations of a function that inhabit the same scope. Declarations that inhabit different scopes have completely distinct sets of default arguments. ...

The problem is that the out-of-line declaration of N::f inhabits the global namespace scope, not the scope of namespace N.

A similar problem exists for inheriting array bounds per 9.3.4.5 [dcl.array] paragraph 8:

Furthermore, if there is a reachable declaration of the entity that inhabits the same scope in which the bound was specified, an omitted array bound is taken to be the same as in that earlier declaration, and similarly for the definition of a static data member of a class.

Possible resolution:

  1. Change in 6.4.1 [basic.scope.scope] paragraph 2 as follows:

    ... The host scope of a declaration is the inhabited scope if that scope is a block scope and the target scope otherwise. An entity belongs to a scope S if S is the target scope of a declaration of the entity.
  2. Change in 9.3.4.5 [dcl.array] paragraph 8 as follows:

    Furthermore, if there is a reachable declaration of the entity that inhabits has the same host scope in which the bound was specified, an omitted array bound is taken to be the same as in that earlier declaration, and similarly for the definition of a static data member of a class.
  3. Change in 9.3.4.7 [dcl.fct.default] paragraph 4 as follows:

    For non-template functions, default arguments can be added in later declarations of a function that inhabit have the same host scope. Declarations that inhabit have different host scopes have completely distinct sets of default arguments. ...
  4. Change in 9.3.4.7 [dcl.fct.default] paragraph 9 as follows:

    When an overload set contains a declaration of a function that inhabits a whose host scope is S, any default argument associated with any reachable declaration that inhabits whose host scope is S is available to the call.
  5. Change in 12.2.4.1 [over.match.best.general] paragraph 4 as follows:

    If the best viable function resolves to a function for which multiple declarations were found, and if any two of these declarations inhabit have different host scopes and specify a default argument that made the function viable, the program is ill-formed. [ Example: ...
      void use() {
       f(3);       // OK, default argument was not used for viability
       f();        // error: found default argument twice
      }
    
      int g(int, int = 0, int = 0);
      void h() {
        using ::g;
        int g(int, int, int = 1);
        int a = g(0); // OK, block scope declaration does not make the function viable;
               //uses default arguments of declaration whose host scope is the global namespace
      }
    
    -- end example ]