C++17和C++20在模板友元函数中一元和二元运算符的区别

Jay*_*Lee 8 c++ templates friend c++17 c++20

我在 C++20 中有以下 MWE clang++ -std=c++2a,其中我定义了类内一元运算-符和friend-ed 二元-运算符:

template<typename T>
class vec;

template<typename T>
vec<T> operator-(const vec<T>&, const vec<T>&);

template<typename T>
class vec {
public:
    vec() {}
    vec operator-() const { return vec(); }
    friend vec operator-<>(const vec&, const vec&);
};

template<typename T>
vec<T> operator-(const vec<T>& lhs, const vec<T>& rhs) { return vec<T>(); }

int main()
{
    vec<int> v;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,这会导致 C++17 中出现以下错误:

main.cpp:12:16: error: friends can only be classes or functions
    friend vec operator-<>(const vec&, const vec&);
               ^
main.cpp:12:25: error: expected ';' at end of declaration list
    friend vec operator-<>(const vec&, const vec&);
                        ^
                        ;
Run Code Online (Sandbox Code Playgroud)

Apple clang version 11.0.3 (clang-1103.0.32.59).

当我删除类内一元运算符时,或者当我通过-std=c++2a.

在 C++17 中是什么导致了这个问题,C++20 如何解决这个问题?任何帮助将不胜感激!

Oli*_*liv 1

这是由于名称查找在类上下文中进行的方式所致。在友元声明符中查找名称的方式与在任何成员声明符中查找名称相同。此处适用的相关查找规则是:

在 [basic.lookup.unqual] 中列出的所有情况下,都会按照每个相应类别中列出的顺序在范围中搜索声明;一旦找到名称的声明,名称查找就会结束。如果没有找到声明,则该程序格式错误。

在 X 的完整类上下文 ([class.mem]) 之外的类 X 定义中使用的名称应通过以下方式之一声明:

  • 在类 X 中使用之前或者是 X 基类的成员 ([class.member.lookup]),或者
  • [...]
  • 如果 X 是命名空间 N 的成员,或者是属于 N 的成员的类的嵌套类,或者是局部类或属于 N 的成员的函数的局部类内的嵌套类,则在定义之前类 X 位于命名空间 N 或 N 的封闭命名空间之一中。

意思就是:

  1. 首先在类范围内查找成员名称的名称;

  2. 如果先前的查找失败,则在封闭的命名空间范围中查找名称。

当编译器在友元声明中找到名称时,operator-它会在类上下文中执行名称查找(不完整)。它找到一元减运算符并停在那里。

之后,编译器应用以下规则来确定该名称是否operator -可以是模板名称C++17/[temp.name]/3

名称查找发现名称是模板名称或运算符函数 id 或文字运算符 id 引用一组重载函数,其中任何成员都是函数模板,如果后面跟着 < , < 始终被视为模板参数列表的分隔符,而永远不会被视为小于运算符。[...]

查找没有找到任何模板,因此在友元声明中operator -不应命名模板。编译器准确地抱怨<该名称后面的标记,该标记不应该出现在那里。

新的 C++20 规则使编译器更倾向于解释名称引用模板,C++20 standard/[temp.names]/2

如果名称查找找到模板名称或包含函数模板的重载集,则认为名称引用模板。如果名称是一个 unqualified-id 后跟 < 且名称查找要么找到一个或多个函数,要么什么也没找到,则该名称也被视为引用模板。

在类作用域中的名称查找vec找到一个函数名称,并且该名称后面跟着一个<字符,因此该名称引用了一个模板。