Clang refuses to compile the following code (godbolt) while gcc sees no problem with it. Clang error message is shown below line marked (2):
namespace // Overall anonymous namespace is required and cannot be removed.
{
template <typename T>
struct Friend;
namespace ns
{
class Secret
{
template <typename T>
friend struct Friend; // (1)
static void foo() {}
};
} // namespace ns
template <typename T>
struct Friend
{
void bar()
{
ns::Secret::foo(); // (2)
// Error at line (2):
// 'foo' is a private member of '(anonymous namespace)::ns::Secret'
}
};
} // anonymous namespace
The reason, I assume, is that line (1) is treated as a declaration of a new class template struct ns::Friend<T>
rather than a reference to ::(anonymous namespace)::Friend<T>
.
The questions are:
- Who's right -- clang or gcc?
- If clang is right, then is there a way to make it understand that line (1) doesn't introduce new class template, but refers to existing one?
CodePudding user response:
Yes, you are right that simply friend struct Friend
is a declaration of a templated class ::(anonymous namespace)::ns::Friend
. Both compilers are right, as when you attempt to use Friend<T>::bar()
gcc will complain about access as well (clang just checks a lot earlier than gcc)
To specify the class template in the global namespace, you must write that out:
template <typename T>
friend struct ::Friend; // (1)
This works for GCC, but clang seems broken and doesn't find Friend
in the global namespace.
These two workarounds seem to work:
// Make the entire unnamed namespace an inline namespace
inline namespace
{
// ...
}
// Add an explicit using directive to help clang find Friend in `::`
namespace {
template<typename T>
struct Friend;
}
using ::Friend;
namespace {
namespace ns {
// ...
}
}