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?
CodePudding user response:
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.
CodePudding user response:
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)