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)
Related
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 {
// ...
}
}
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"};
}
The title is a mouthful, but basically I wrote something like this:
enum EnumType{ValA, ValB};
template<EnumType> class A {};
template<>
class A<ValA>
{
private:
double param;
public:
A(double param);
};
template<>
A<ValA>::A(double param)
{
// Do Stuff
}
and when I try to compile it I get:
error: template-id 'A<>' for 'A<(EnumType)0u>::A(double)' does not
match any template declaration
Am I doing this wrong?
After searching online for similar cases, I tried to remove template<> (even though I don't understand why this would work), but then I get
multiple definition of 'A<(EnumType)0u>::A(double)'
I guess that I can replace template<> by inline (I tried and it compiles), but that doesn't feel like the proper way to do it (or if it is, I don't understand why).
Can someone explain to me what is wrong with what I wrote, why changing this seems to work, and what's the proper way to do it ?
Can someone explain to me what is wrong with what I wrote, why changing this seems to work, and what's the proper way to do it ?
The standard says:
Members of an explicitly specialized class template are defined in the same manner as members of normal classes, and not using the template<> syntax.
Therefore, in your case you must use:
A<EnumType::ValA>::A(double param)
{
// Do Stuff
}
No template<> at all is just fine. That's because you are actually specializing a (special) member function (the constructor) of an explicitly specialized class template.
See it on coliru.
It would have been different if no explicit specialization was given.
As a minimal working example:
enum EnumType{ValA, ValB};
template<EnumType> class A
{
private:
double param;
public:
A(double param);
};
template<>
A<EnumType::ValA>::A(double)
{
// Do Stuff
}
int main() {
A<EnumType::ValA> a{0.};
}
In this case, template<> is required before the definition of the constructor because you are not defining a specialization of a member function of an already specialized class template.
You missed a semicolon (;) at the the end of class definition.
And the non template member function can be defined this way:
A<ValA>::A(double param) {
// Do Stuff
}
Informally, the template parameter list is only written when necessary, for example, for defining a member function template of a class template, the two template parameter list should all be written
template<class U, class V>
class A{
template <class T>
A();
};
template<class U, class V>
template <class T>
A<U, V>::A() {}
and a empty template parameter list is needed for a explicit specialisation of function template (which, i guess, is the reason why you use so here), informally because it tells the compiler that this is not a function overloading.
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;
};
An answer to a a question I had about deleting functions mentioned how member function templates can't be specialized at class scope. That led me to wonder if it's possible for a member function template specialization to have a different access level than the main template. In the code below, I'm trying to have a private specialization of a public member function template:
#include <iostream>
class Foo {
public:
template<typename T>
void func(T) { std::cout << "Public\n"; }
private:
template<>
void func<char>(char) { std::cout << "Private\n"; }
friend int main();
};
int main()
{
Foo f;
f.func(10);
f.func('a');
}
With the latest MSVC, this compiles, runs, and produces the expected output:
Public
Private
With g++ 4.8 and Clang 3.2, the code is rejected. Clang says this:
error: explicit specialization of 'func' in class scope
void func<char>(char) { std::cout << "Private\n"; }
^
Presumably g++ and Clang are using 14.7.3/2 of C++11 as the basis for their behavior, but I think there might be a little wiggle room, because 3.3.6/3 says that the global scope is a namespace, and the global namespace (indirectly) encloses the template specialization.
My question isn't about these parts of the Standard or about any of these compilers' behaviors, though, it's about whether it is possible for a member function template to have a specialization that has a different access level than the general template. For example, is it possible to have a public member function template and a private specialization of that template?
We can always do it manually.
Some random SFINAE machinery:
#include <iostream>
#include <utility>
#include <type_traits>
template<typename T> constexpr bool IsInt() { return std::is_same<T,int>::value; }
template<std::size_t>
struct SecretEnum {
enum class hidden {};
};
template<bool b, int i=1> using EnableIf = typename std::enable_if<b,typename SecretEnum<i>::hidden>::type;
class Foo {
public:
template<typename T, EnableIf< !IsInt<T>(), 1 >...>
void func(T) { std::cout << "Public\n"; }
private:
template<typename T, EnableIf< IsInt<T>(), 2 >...>
void func(T) { std::cout << "Private with int\n"; }
friend int main();
};
int main()
{
Foo f;
f.func(10);
f.func('a');
}
now this trick does not work with clang because of how I did the SFINAE and method distinguishing last I checked. But that can be replaced with other similar tricks (like pointer based default arguments in the second argument -- replace EnableIf< IsInt<T>(), 2 >... with EnableIf< IsInt<T>(), 2 >* = nullptr or somesuch for clang. I just find it less appealing.)
So what is going on above? I have two different overloads for func. Both are template functions with one argument that is a T, and a pack of some secret enum whose type is valid if and only if T matches the IsInt<T>() or !IsInt<T>() test respectively. The type of the packs differ in the two cases (one if them is SecretEnum<2>::hidden, the other is SecretEnum<1>::hidden), so their signatures are sufficiently different to satisfy most C++11 compilers (clang considers them to be identical last I checked, generating errors, I believe clang is wrong).
When you invoke func<blah>, it checks to see which (if any) of the two func are appropriate. As their conditions are exact opposites of each other, only one of them is ever the proper one.
In effect, we are doing manual specialization.
In C++1y, we may be able to template<IsInt T> and template<IsNotInt T> if the stars align properly and the concepts lite that gets into the technical report lets this work.