This is due to the way name look-up proceeds inside class context. Look up for names inside friend declarator are looked-up as in any member declarators. The pertinent lookup rules that apply here are:
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.
Which means that:
names are first look up in the class scope, for member names;
if this previous look up fails, name are looked up in the enclosing namespace scope.
When the compiler find the name operator-
in the friend declaration it performs name look in the class context (incomplete). It finds the unary minus operator and stop there.
After that the compiler applies the following rule to determine if the name operator -
can be a template name 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. [...]
Lookup did not find any template, so inside the friend declaration operator -
is not supposed to name a template. The compiler complains precisely at the <
token that follows this name, which is not supposed to be there.
A new C++20 rule makes the compiler more inclined to interpret that a name refers to template, 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.
Name lookup in class vec
scope find a function name and this name is followed by a <
character, so this name refers to a template.