C++17 和 C++20 在具有一元和二元运算符的模板友元函数中的区别
Difference of C++17 and C++20 in template friend function with unary and binary operators
我在 C++20 中有以下带有 clang++ -std=c++2a
的 MWE,其中我定义了 in-class 一元 -
运算符和 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;
}
但是,这会导致 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&);
^
;
与 Apple clang version 11.0.3 (clang-1103.0.32.59)
.
当我删除 in-class 一元运算符时,或者当我通过 -std=c++2a
.
使用 C++20 时,错误消失了
是什么导致了 C++17 中的这个问题,C++20 是如何解决这个问题的?
任何帮助将不胜感激!
这是由于名称查找在 class 上下文中进行的方式所致。在 friend 声明符中查找名称与在任何成员声明符中查找一样。适用于此处的相关查找规则是:
In all the cases listed in [basic.lookup.unqual], the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.
A name used in the definition of a class X outside of a complete-class context ([class.mem]) of X shall be declared in one of the following ways:
- before its use in class X or be a member of a base class of X ([class.member.lookup]), or
- [...]
- if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within a local class of a function that is a member of N, before the definition of class X in namespace N or in one of N's enclosing namespaces.
这意味着:
名称首先在 class 范围内查找成员名称;
如果之前的查找失败,将在封闭的命名空间范围内查找名称。
当编译器在友元声明中找到名称 operator-
时,它会在 class 上下文中执行名称查找(不完整)。它找到一元减号运算符并停在那里。
之后,编译器应用以下规则来确定名称 operator -
是否可以是模板名称 C++17/[temp.name]/3
After name lookup finds that a name is a template-name or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template, if this is followed by a <, the < is always taken as the delimiter of a template-argument-list and never as the less-than operator. [...]
查找未找到任何模板,因此在朋友声明中 operator -
不应该命名模板。编译器准确地抱怨这个名字后面的 <
标记,它不应该在那里。
新的 C++20 规则使编译器更倾向于解释名称指的是模板,C++20 standard/[temp.names]/2:
A name is considered to refer to a template if name lookup finds a
template-name or an overload set that contains a function template. A
name is also considered to refer to a template if it is an
unqualified-id followed by a < and name lookup either finds one or
more functions or finds nothing.
class vec
范围内的名称查找找到一个函数名称,这个名称后跟一个 <
字符,所以这个名称指的是一个模板。
我在 C++20 中有以下带有 clang++ -std=c++2a
的 MWE,其中我定义了 in-class 一元 -
运算符和 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;
}
但是,这会导致 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&);
^
;
与 Apple clang version 11.0.3 (clang-1103.0.32.59)
.
当我删除 in-class 一元运算符时,或者当我通过 -std=c++2a
.
是什么导致了 C++17 中的这个问题,C++20 是如何解决这个问题的? 任何帮助将不胜感激!
这是由于名称查找在 class 上下文中进行的方式所致。在 friend 声明符中查找名称与在任何成员声明符中查找一样。适用于此处的相关查找规则是:
In all the cases listed in [basic.lookup.unqual], the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.
A name used in the definition of a class X outside of a complete-class context ([class.mem]) of X shall be declared in one of the following ways:
- before its use in class X or be a member of a base class of X ([class.member.lookup]), or
- [...]
- if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within a local class of a function that is a member of N, before the definition of class X in namespace N or in one of N's enclosing namespaces.
这意味着:
名称首先在 class 范围内查找成员名称;
如果之前的查找失败,将在封闭的命名空间范围内查找名称。
当编译器在友元声明中找到名称 operator-
时,它会在 class 上下文中执行名称查找(不完整)。它找到一元减号运算符并停在那里。
之后,编译器应用以下规则来确定名称 operator -
是否可以是模板名称 C++17/[temp.name]/3
After name lookup finds that a name is a template-name or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template, if this is followed by a <, the < is always taken as the delimiter of a template-argument-list and never as the less-than operator. [...]
查找未找到任何模板,因此在朋友声明中 operator -
不应该命名模板。编译器准确地抱怨这个名字后面的 <
标记,它不应该在那里。
新的 C++20 规则使编译器更倾向于解释名称指的是模板,C++20 standard/[temp.names]/2:
A name is considered to refer to a template if name lookup finds a template-name or an overload set that contains a function template. A name is also considered to refer to a template if it is an unqualified-id followed by a < and name lookup either finds one or more functions or finds nothing.
class vec
范围内的名称查找找到一个函数名称,这个名称后跟一个 <
字符,所以这个名称指的是一个模板。