Making a class template declared in an outer anonymous namespace a friend - c++

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?

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 {
// ...
}
}

Related

How to fix gcc warning "friend declaration declares a non-template function"

So I have some code here that compiles with gcc, clang, and msvc:
#include <cstdio>
#include <type_traits>
struct c_class;
template <class T> struct holder { friend auto adl_lookup(holder<T>); };
template <class C, class T> struct lookup {
friend auto adl_lookup(holder<T>) { return holder<C>{}; }
};
struct cpp_class : lookup<cpp_class, c_class *> {
cpp_class() {}
};
int main() {
static_assert(std::is_same<holder<cpp_class>,
decltype(adl_lookup(holder<c_class *>{}))>{},
"Failed");
}
The reason adl_lookup is defined in the lookup class instead of the holder class is so that you can do a "reverse" lookup from c_class to cpp_class when you inherit from the CRTP class lookup<cpp_class, c_class *>. So the friend function can't be moved to the holder class.
However, on gcc I get a warning about non template friend function:
<source>:9:37: warning: friend declaration 'auto adl_lookup(holder<T>)' declares a non-template function [-Wnon-template-friend]
9 | friend auto adl_lookup(holder<T>);
| ^
<source>:9:37: note: (if this is not what you intended, make sure the function template has already been declared and add '<>' after the function name here)
If I try to fix this by forward declaring the function and then using <>, it doesnt compile with gcc or msvc(although it does compile with clang):
#include <cstdio>
#include <type_traits>
struct c_class;
template <class T> struct holder;
template <class T> auto adl_lookup(const holder<T> &);
template <class T> struct holder {};
template <class C, class T> struct lookup {
friend auto adl_lookup<>(const holder<T> &) { return holder<C>{}; }
};
struct cpp_class : lookup<cpp_class, c_class *> {
cpp_class() {}
};
int main() {
static_assert(std::is_same<holder<cpp_class>,
decltype(adl_lookup(holder<c_class *>{}))>{},
"Failed");
}
Am I using standard-compliant C++ here(in both snippets)? Is there a reason to be concerned about gcc's warning about non-template friend or is it just a false positive that I can safely ignore?
The second snippet is ill-formed, because a friend declaration cannot be a definition of a template specialization. An open clang bug report for accepting this is here.
The first one seems valid to me.
The warning by GCC is annoying, because defining a non-template function as friend is what you want to do here. Unfortunately I don't think there is any way to indicate in code that this is really what you want to do, but you can disable the warning with -Wno-non-template-friend. According to the documentation it is there for historical reasons, to identify pre-ISO-C++ compatibility issues where the syntax had a different meaning.
You should be aware that the ability to use friend injections of this kind to enable stateful metaprogramming may be considered unintended feature of the language and could maybe (I don't know) be restricted at some point in the future, see this question.
It is a common mistake, as using if (var = foo()) instead if (var == foo()) even if the code are legal.
Whereas adding extra parents allow to "inform" compiler of intent is the one expected in the =/== case.
if ((var = foo()))
There are no trick syntax to tell that it is really a non-template function that you want to use.
There are still traditional #pragma way to disable warning in specific region for given compiler (see how-to-disable-gcc-warnings-for-a-few-lines-of-code)

How to provide deduction guide for nested template class?

According to [temp.deduct.guide/3]:
(...) A deduction-guide shall be declared in the same scope as the
corresponding class template and, for a member class template, with
the same access. (...)
But below example doesn't seem to compile in both [gcc] and [clang].
#include <string>
template <class>
struct Foo {
template <class T>
struct Bar {
Bar(T) { }
};
Bar(char const*) -> Bar<std::string>;
};
int main() {
Foo<int>::Bar bar("abc");
static_cast<void>(bar);
}
What is the correct syntax of deduction guide for nested template class? Or maybe this one is correct but it isn't yet supported by the compilers?
Similar syntax but without nested class compiles fine both in gcc and clang:
#include <string>
template <class T>
struct Bar {
Bar(T) { }
};
Bar(char const*) -> Bar<std::string>;
int main() {
Bar bar("abc");
static_cast<void>(bar);
}
[temp.deduct.guide] includes the sentence:
A deduction-guide shall be declared in the same scope as the corresponding class template and, for a member class template, with the same access.
This suggests that your example should work - deduction guides are explicitly supported for member class templates, as long as they're declared in the same scope and access (which would be the class scope and public - check and check).
This is gcc bug 79501 (filed by Richard Smith).
If you really need a temporary quick-fix, consider using compiler-specific instructions.
Here, on godbolt
The key is to handle the behavioral divergence between GCC and Clang by adding a compiler-specific directive.
This is way sub-optimal, but if you are blocked in your project, it may help you wait for compiler(s) patch.
See my answer to this post : https://stackoverflow.com/a/66888013/10883423
#include <string>
template <class>
struct Foo {
template <class T>
struct Bar {
Bar(T) { }
};
#if __clang__
Bar(char const*) -> Bar<std::string>;
#endif
};
void instanciate_symbols()
{
[[maybe_unused]] auto bar = Foo<int>::Bar{"abc"};
}

Friend of function in dependent scope

Is this code invalid:
template <class T> struct A;
class C {
template <class T> friend void A<T>::foo();
};
In GCC 6.1.0 it says:
error: member 'void A<T>::foo()' declared as friend before type 'A<T>' defined
template <class T> friend void A<T>::foo();
Clang 3.8.0:
warning: dependent nested name specifier 'A<T>::' for friend class declaration
is not supported; turning off access control for 'C' [-Wunsupported-friend]
And Visual Studio 2015 crashes:
fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'f:\dd\vctools\compiler\cxxfe\sl\p1\c\template.cpp', line 8952)
template <class T> friend void A<T>::foo();
More specifically, is A required to be defined before the friend declaration?
template <class T> struct A;
class C {
static void foo();
template <class T> friend void A<T>::f();
};
template <class T> struct A {
void f() { }
};
If so, why?
You refer to A's member function foo. This function is not known to exist yet, because you only forward declare A.
In other words, you'll have to declare A<T>::foo before C, as the GCC message tries to tell you (the other two are rather cryptic). This means you have to declare the complete interface of A before C, instead of only forward declaring it.
Solution
You can declare f as a friend of C in the following way:
class C;
template <class T> struct A {
void f(C const &c);
};
class C {
int i = 42;
public:
static void foo();
template <class T> friend void A<T>::f(C const &c);
};
template <class T> void A<T>::f(C const &c) { std::cout << c.i << std::endl; }
Live Demo
Justification
According to the standard §3.3.2/p6 Point of declaration [basic.scope.pdecl]:
After the point of declaration of a class member, the member name can
be looked up in the scope of its class. [ Note: this is true even if
the class is an incomplete class. For example,
struct X {
enum E { z = 16 };
int b[X::z]; // OK
};
— end note ]
The diagnostic is rather misleading both for GCC and CLANG. The real problem with your code is not the fact that you're trying to access the definition of an incomplete type, but rather is that you can only refer to a class member name only after it has been declared.
The first problem is (I think) from §9.3/7 [class.mfct] and probably some other places in the standard (see clang message below and 101010's answer):
Previously declared member functions may be mentioned in friend declarations.
This problem is similar to the one from this question.
Since you did not declare A<T>::f before C, you cannot declare it has a friend of C.
But there is an hidden problem behing clang's message, if you make A a non-templated class, the message is different:
Incomplete type A in nested name specifier.
Which is closer to gcc message than the actual one, this is because clang's warning is about something else. Per §14.5.4/5 [temp.friend], the standard allows member of class template to be friend, so this must be valid:
template <typename T>
struct A {
void f ();
};
class C {
template <typename T>
friend void A<T>::f(); // Ok, A<T>::f is already declared
void private_member (); // See below
};
But clang still complains:
warning: dependent nested name specifier 'A::' for friend class declaration
is not supported; turning off access control for 'C' [-Wunsupported-friend]
clang does not support such friend declaration, so it simply turns off access control for C, meaning that:
C c;
c.private_member();
Will be "valid" everywhere, which may not be what you want.
Firstly, Forward declaration of a class is not sufficient if you need to use the actual class type, for example, if you need to use it as a base class, or if you need to use the methods of the class in a method.
Since here you try to use its details "foo()", there is no way compiler knows what is A::foo().. Compiler cannot distinguish if it is a typo (or) actual function.. it needs to know the declaration of A::foo() before you can use it.
If you still want to only to forward declare the class and make it a friend, see if friend classes fit your situation
template <class T> struct A;
class C{
template <class T> friend struct A;
};

Template class with a friend function which is inside a nested namespace

I'm trying to create a template class with a friend function which is inside a nested namespace. It works fine if I remove all the namespaces or if I remove all the templatization. But with both in place it won't compile. Let's look at some code:
namespace MyNamespace
{
// Forward declaration
template <typename Type>
class Container;
// Forward declaration
namespace AccessPrivateImplementation
{
template <typename Type>
Type getValue(Container<Type>* container);
}
// Templatized class
template <typename Type>
class Container
{
friend Type AccessPrivateImplementation::getValue(Container<Type>* volume);
private:
Type value;
};
// Friend function inside a namespace
namespace AccessPrivateImplementation
{
template <typename Type>
Type getValue(Container<Type>* container)
{
return container->value;
}
}
}
int main(int argc, char* argv[])
{
MyNamespace::Container<int> cont;
MyNamespace::AccessPrivateImplementation::getValue(&cont);
return 0;
}
The compiler (VS2010) tells me:
error C2248: 'MyNamespace::Container::value' : cannot access private member declared in class 'MyNamespace::Container'
Does anyone have any idea what I'm missing?
The friend declaration inside the Container class template declares a friend non-template function getValue() that lives in the AccessPrivateImplementation namespace.
However, you haven't provided such a function. Instead, what you have in the AccessPrivateImplementation namespace is a function template, whose appropriate specialization you want to be friend of Container<T> (for a given T).
To achieve this, the declaration you need is:
friend Type AccessPrivateImplementation::getValue<>(Container<Type>* volume);
// ^^
Here is a live example that shows your code compiling with the above fix.
As per my comment, if you declare the friend like so it will work:
friend Type AccessPrivateImplementation::getValue<>(Container<Type>* volume);
^^

Using clause on template class

To make referencing types easier you can bring individual types into the current scope with the using clause:
namespace MyCompany
{
namespace MyProject
{
class MyType {};
void myFunc(MyType const& obj) {}
}
}
int main()
{
using MyCompany::MyProject::MyType;
using MyCompany::MyProject::myFunc;
MyType plop;
myFunc(plop);
}
The problem is if the class is a template class (or function). Is it possible to bring these into the current scope without fully instantiating them?
namespace MyCompany
{
namespace MyProject
{
template<typename T>
class MyType {};
template<typename T>
void myFunc(MyType<T> const& obj) {}
}
}
int main()
{
// Can I bring the templates into scope?
// Or do I need to fully instantiate each version in the using clause?
using MyCompany::MyProject::MyType; ????
using MyCompany::MyProject::myFunc; ????
MyType plop;
myFunc(plop);
}
Both VC10 & gcc run the code ok with different instantiations after the using declaration.
So it seems the using declaration has brought the whole template to the scope.
Also in c++0x standard n3290 7.3.3 - 5
A using-declaration shall not name a template-id. [ Example:
struct A {
template <class T> void f(T);
template <class T> struct X { };
};
struct B : A {
using A::f<double>; // ill-formed
using A::X<int>; // ill-formed
};
which seems to suggest that using should not be used with specific template-id.