Search code examples
c++language-lawyerc++23

Why can't you define a class or function in a non-enclosing scope?


Consider

namespace M {
    void f();
    struct S;
}
namespace N {
    void M::f() {}
    struct M::S {};
}

This code is rejected by all compilers I tested, because M::f and M::S are only allowed to be defined in an enclosing scope. The standard also contains an example that implies that a function may only be defined in an enclosing scope.

However, I can't figure out where in the standard to find the actual rule. In C++20 there was [class.pre]/3:

If a class-head-name contains a nested-name-specifier, the class-specifier shall refer to a class that was previously declared directly in the class or namespace to which the nested-name-specifier refers, or in an element of the inline namespace set (9.8.2) of that namespace (i.e., not merely inherited or introduced by a using-declaration), and the class-specifier shall appear in a namespace enclosing the previous declaration. In such cases, the nested-name-specifier of the class-head-name of the definition shall not begin with a decltype-specifier.

There was also a rule about member functions, but I couldn't find one for free functions. P1787R6 removed the wording shown above, but the replacement wording doesn't seem to imply the same restriction. Here's what [class.pre] says in the C++23 draft:

If a class-head-name contains a nested-name-specifier, the class-specifier shall not inhabit a class scope. If its class-name is an identifier, the class-specifier shall correspond to one or more declarations nominable in the class, class template, or namespace to which the nested-name-specifier refers; they shall all have the same target scope, and the target scope of the class-specifier is that scope. [...]

In the example above, the nested-name-specifier M:: in the attempted definition of M::S denotes the namespace M, and S is nominable in M at this point because S was previously declared in M; [basic.scope.scope]/6.

Since I can't find any rule in the C++23 draft to forbid definitions in non-enclosing scopes, I might think that P1787R6 made it allowed. However, since compilers still don't accept this code, I think it's more likely that the rule still exists but is in some place that I've missed (or is phrased more subtly than it used to be).


Solution

  • [expr.prim.id.qual]p2:

    A declaration that uses a declarative nested-name-specifier shall be a friend declaration or inhabit a scope that contains the entity being redeclared or specialized.

    Where your two redeclarations do not "inhabit a scope that contains the entity being redeclared or specialized". (Your original declarations inhabit the namespace scope ::M, and the defining declarations inhabit ::N, where ::N does not contain ::M).