I have recently come across this answer about forward declaration of the class in the unnamed namespace, and I was surprised that it indeed compiles and seems to work with Clang. I thought that every instance of unnamed namespace (also within translation unit) has unique identifier.
Reading the [namespace.unnamed] section from the standard didn't really clear my doubts, because (emph. mine):
all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the translation unit
so I naturally assumed that
namespace {
int x;
}
namespace {
int y;
}
has to translate to:
namespace unique1 {
int x;
}
namespace unique2 {
int y;
}
However, cppreference claims that
The unique name is unique over the entire program, but within a translation unit each unnamed namespace definition maps to the same unique name: multiple unnamed namespace definitions in the same scope denote the same unnamed namespace.
which seems to contradict the standard.
I tried to compile the following example with gcc-15.1 (doesn't compile) and clang-20.1 (compiles and outputs expected result 42
): https://godbolt.org/z/7as6bzxGW
#include <iostream>
namespace {
class AnonClass;
}
class A {
public:
void print() {
std::cout << a << '\n';
}
private:
int a;
friend class AnonClass;
};
namespace {
class AnonClass {
public:
AnonClass(A* parent) { parent->a = 42; }
};
}
int main() {
A parent;
AnonClass ac{&parent};
parent.print();
}
Which compiler is correct? Should both unnamed namespaces have the same unique identifier, or should they be considered different?
The wording of [namespace.unnamed] requires a careful reading with slightly different focus:
An unnamed-namespace-definition behaves as if it were replaced by
(...) namespace unique (...)
(...) and all occurrences of unique in a translation unit are replaced by the same identifier
This means that all the unnamed namespaces in a same translation unit refer to the same unnamed namespace. This is consistent with [basic.namespace.general] which guarantees that:
the definition of a namespace can be split over several parts of one (...) translation units (...).
The explanation then adds that the unnamed namespace is different from all the other (named) namespaces in the translation unit. This is to say that you could even have a named namespace unique
that wouldn't of course collide with the unnamed namespace:
An unnamed-namespace-definition behaves as if it were replaced by
(...)
(...) and this identifier differs from all other identifiers in the translation unit.
There is some residual ambiguity in this section: this wording doesn't say that the unnamed namespace of one translation unit does not collide with the unnamed namespace of the other translation units. This is implied by [basic.link]/4:
An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage. All other namespaces have external linkage.
In this respect, the former definition of C++11 was less ambiguous as it was stated directly in [namespace.unnamed], although it had other ambiguities:
all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the entire program.
Your code snippet compiles on MSVC and CLANG and not in GCC. This is not only related to the unnamed namespace. Indeed, you could achieve the same result by replacing the unnamed namespace with a pseudo unique namespace X as specified:
namespace X {
class AnonClass;
}
using namespace X;
...
Indeed, despite the forward definition of AnonClass
and the using clause, GCC fails to recognise that the friend refers to X::AnonClass
. This seems clearly a restriction which is not aligned with the standard. Again, MSVC and CLANG work well and only GCC shows this issue. With a named namespace you can use a qualified friend name to disambiguate (which shouldn't be required) whereas this is not possible for unnamed namespaces.
Online demo (comment out the qualified friend to see that GCC still complains).