Question Details

No question body available.

Tags

c++ namespaces language-lawyer c++20 argument-dependent-lookup

Answers (1)

December 14, 2025 Score: 22 Rep: 19,939 Quality: Expert Completeness: 100%

I was thinking that GCC trunk is correct because it implements the post-C++20 rule to the letter. But I found an old CWG issue that contradicts with the new behavior (see below). I can't tell whether GCC is correct and the standard committee changed its mind, or GCC is incorrect and either the post-C++20 wording is defective or both I and GCC maintainer misread the new wording.

Before C++20, the specification says (N4659 [basic.lookup.argdep]/3):

Let X be the lookup set produced by unqualified lookup and let Y be the lookup set produced by argument dependent lookup (defined as follows). If X contains

  • [...]

then Y is empty. Otherwise Y is the set of declarations found in the namespaces associated with the argument types as described below.

When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier except that:

  • [...]
  • Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup.
  • [...]

Here X contains nothing, so Y consists of the declarations found in A's associated namespace, which is the global namespace, which doesn't contain a declaration of f. The lookup finds nothing.

After C++20 (specifically, after the post-C++20 paper P1787R6), the specification says (N4950 [basic.lookup.argdep]/4):

Argument-dependent lookup finds all declarations of functions and function templates that

  • are found by a search of any associated namespace, or
  • are declared as a friend ([class.friend]) of any class with a reachable definition in the set of associated entities, or
  • are exported, are attached to a named module M ([module.interface]), do not appear in the translation unit containing the point of the lookup, and have the same innermost enclosing non-inline namespace scope as a declaration of an associated entity attached to M ([basic.link]).

Note that in the second bullet, for functions that are declared as a friend, there's no restriction to the namespace. Hence it seems that ADL should find the declaration of ns::f.

I found GCC commit 8f6c9ebc7d7 which seems to be the commit that implements the new rule. It mentions that "in C++20 P1787 changed [basic.lookup.argdep]/4.2 to directly include all friends in the lookup", which agrees with my analysis.

I also found editorial issue #5302 against the draft standard, which presents a similar example. But this issue doesn't have a definitive outcome.

Lastly, I found CWG 143, dated 1999. It says ADL works by finding "invisible" declarations in the namespace in which the associated classes are members, and not by finding friend declarations in associated classes (with an example similar to the one in this question). If the committee didn't change its mind, this means GCC's new behavior is incorrect.