如何修复 gcc 警告 "friend declaration declares a non-template function"

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

所以我这里有一些用 gcc、clang 和 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");
}

adl_lookup 定义在 lookup class 而不是 holder class 中的原因是您可以进行“反向”查找当您从 CRTP class lookup<cpp_class, c_class *> 继承时,从 c_classcpp_class。所以友元函数不能移到holder class.

但是,在 gcc 上,我收到关于非模板友元函数的警告:

<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)

如果我尝试通过前向声明函数然后使用 <> 来修复此问题,它不会使用 gcc 或 msvc 进行编译(尽管它使用 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");
}

我在这里(在两个片段中)使用了符合标准的 C++ 吗?是否有理由担心 gcc 对非模板朋友的警告,或者它只是一个我可以放心忽略的误报?

第二个片段是 ill-formed,因为 friend 声明不能是模板特化的定义。接受这个的 open clang 错误报告是 here.

第一个对我来说似乎有效。

GCC 的警告很烦人,因为在这里定义一个 non-template 函数作为 friend 是你想要做的。不幸的是,我认为没有任何方法可以在代码中表明这确实是您想要做的,但是您可以使用 -Wno-non-template-friend 禁用警告。根据 the documentation 它是出于历史原因,用于识别语法具有不同含义的 pre-ISO-C++ 兼容性问题。

您应该知道,使用这种友元注入来启用有状态元编程的能力可能被认为是该语言的意外特性,并且可能(我不知道)在将来的某个时候受到限制,参见

这是一个常见的错误,因为使用 if (var = foo()) 而不是 if (var == foo()) 即使代码是合法的。

而在 =/== 情况下,添加额外的父项允许“通知”编译器意图。

if ((var = foo()))

没有技巧语法可以表明它确实是您要使用的 non-template 函数。

对于给定的编译器,仍然有传统的 #pragma 方法来禁用特定区域的警告(参见 how-to-disable-gcc-warnings-for-a-few-lines-of-code