C++ Logo

std-discussion

Advanced search

Re: forward declaration inside another function in a namespace

From: Andrew Schepler <aschepler_at_[hidden]>
Date: Sat, 29 Jun 2024 19:53:29 -0400
On Sat, Jun 29, 2024 at 8:35 AM Federico Kircheis via Std-Discussion <
std-discussion_at_[hidden]> wrote:

> Hello to everyone,
>
> I am failing to understand if following code is really invalid.
> I've looked at the standard, but I am missing where the relevant
> information is.
>

The entities found when searching for a name in a namespace scope are not
always the same as the entities which are members of that namespace (and
have names). The differences come up with using-declarations,
using-directives, friend declarations, extern variables in block scope, and
this situation: function declarations in block scope.

The relevant rule in C++20 and earlier was [basic.link]/7:

> (6) The name of a function declared in block scope and the name of a
> variable declared by a block scope extern declaration have linkage. ...



(7) When a block scope declaration of an entity with linkage is not found
> to refer to some other declaration, then that entity is a member of the
> innermost enclosing namespace. However such a declaration does not
> introduce the member name in its namespace scope.


C++23 revised the all scope and name lookup language to be more precise.
The same rule is now found in [dcl.meaning.general]/(3.5):

> If the declaration inhabits a block scope *S* and declares a function
> ([dcl.fct]) or uses the extern specifier, the declaration shall not be
> attached to a named module ([module.unit]); its target scope is the
> innermost enclosing namespace scope, but the name is bound in *S*.

 [Example 3:

namespace X {

  void p() {

    q(); // error: q not yet declared

    extern void q(); // q is a member of namespace X

    extern void r(); // r is a member of namespace X

  }



  void middle() {

    q(); // error: q not found
>
  }



  void q() { /* ... */ } // definition of X::q

}

-- end example]

Here "target scope" determines whether a declaration is a member of a
namespace or a class or neither ([basic.scope.scope]/2). A name "bound" in
a scope influences the name lookup rules: [basic.lookup.general]/3 defines
"single search" in terms of "bound" names, [class.member.lookup]/1 defines
a "search in a scope" in terms of "single search". In particular for your
examples, the qualified-id ::a::bar requires that the name "bar" was bound
in namespace "a" or in an inline namespace of "a", or can be found via a
using-directive ([namespace.qual]), but it was not.

-- Andrew Schepler

Those snippets compile fine
>
> 1)
> ~~~~
> namespace a {
> int bar(int);
> int foo() {
> return ::a::bar(12);
> }
> int bar(int) {
> return 1;
> }
> }
> ~~~~
> 2)
> ~~~~
> namespace a {
> int foo() {
> int bar(int);
> return bar(12);
> }
> int bar(int) {
> return 1;
> }
> }
> ~~~~
>
> but this one does not(!)
>
> 3)
> ~~~~
> namespace a {
> int foo() {
> int bar(int);
> return ::a::bar(12);
> }
> int bar(int) {
> return 1;
> }
> }
> ~~~~
>
> both gcc and clang suggest that "'bar' is not a member of 'a'" / that
> there is "no member named 'bar' in namespace 'a'" (replacing "::a" with
> "a" does not change the outcome).
>
> Since the function in namespace a is called (even if there is a bar
> function in the global namescope), I am failing to understand why it
> should not be possible to qualify the function call with the namespace.
>
> Especially if there is a function at global scope, removing the forward
> declaration changes the behavior of the program.
>
>
> ~~~~
> int bar(int){
> return 0;
> }
>
> namespace a {
> int foo() {
> // if removed, the line after this comment calls ::bar,
> // and not ::a::bar
> // Since the function call is not qualified,
> // someone might even think it it already calling ::bar
> int bar(int num);
> return bar(12);
> }
> }
> ~~~~
>
> Best
>
> Federico
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>

Received on 2024-06-29 23:53:42