There used to be a paragraph in the standard which stated that
the names of a namespace-scope friend functions of a class template specialization are not visible during an ordinary lookup unless explicitly declared at namespace scope. Such names may be found under for associated classes.
template <typename T> struct number {
number(int);
friend number gcd(number x, number y) { return 0; }
};
void g() {
number<double> a(3), b(4);
a = gcd(a, b); // finds gcd becuase numer<double> is an associated class,
// making gcd visible in its namespace (global scope)
b = gcd(3, 4); // error: gcd is not visible
}
This feature has been used to capture and retrieve a metaprogramming state at compile time.
template <int X>
struct flag {
friend consteval int f(flag<X>);
};
template <int X, int N>
struct injecter {
friend consteval int f(flag<X>) { return N; }
};
template <int N = 0, auto = []{}>
consteval auto inject() {
if constexpr (requires { f(flag<X>{}); }) {
return inject<N+1>();
} else {
void(injecter<X>{});
return f(flag<X>{});
}
}
static_assert(inject() == 0);
static_assert(inject() == 1);
static_assert(inject() == 2);
static_assert(inject() == 3);
// ...
As far as I know, there is an issue from CWG which aimed to make such behaviour ill-formed although the mechanism for prohibiting them is as yet undetermined. However, as the paragraph seems to be absent from the latest draft, I'm wrondering if friend injections will be ill-formed in C++23.