Это связано с тем, как поиск имени происходит внутри контекста класса. Ищите имена внутри декларатора друзей, ищите, как и в любых деклараторах членов. Соответствующие правила поиска, которые применяются здесь:
In во всех случаях, перечисленных в [basi c .lookup.unqual], в областях выполняется поиск объявления в порядке, указанном в каждой из соответствующих категорий; Поиск имени заканчивается, как только для имени найдено объявление. Если объявление не найдено, программа имеет неправильный формат.
Имя, используемое в определении класса X вне контекста полного класса ([class.mem]) X, должно быть объявлено одним из следующих способов:
- до его использования в классе X или быть членом базового класса X ([class.member.lookup]), или
- [...]
- , если X является членом пространства имен N, или является вложенным классом класса, который является членом N, или является локальным классом или вложенным классом в локальном классе функции, которая является членом N, до определения класса X в пространстве имен N или в одном из охватывающих пространств имен N.
Это означает, что:
имена сначала ищутся в области класса для имен членов ;
, если предыдущий поиск завершился неудачно, имя ищется в охватывающей области пространства имен.
Когда компилятор находит имя operator-
в объявлении друга он выполняет поиск имени в контексте класса (неполный). Он находит унарный оператор минус и останавливается на нем.
После этого компилятор применяет следующее правило, чтобы определить, может ли имя operator -
быть именем шаблона C ++ 17 / [temp.name] / 3
После поиска по имени обнаруживает, что имя является именем шаблона или что идентификатор-функции-оператора или идентификатор-литерала-оператора относится к набору перегруженных функций любого члена из которых является шаблоном функции, если за ним следует <, то <всегда используется как разделитель списка аргументов-шаблонов и никогда не используется как оператор «меньше». [...] </p>
Поиск не нашел ни одного шаблона, поэтому в объявлении друга operator -
не предполагается называть шаблон. Компилятор жалуется именно на токен <
, следующий за этим именем, которого там не должно быть.
Новое правило C ++ 20 делает компилятор более склонным интерпретировать, что имя относится к шаблону, C ++ 20 standard / [temp.names] / 2 :
Считается, что имя относится к шаблону, если поиск имени обнаруживает имя шаблона или набор перегрузки который содержит шаблон функции. Имя также считается относящимся к шаблону, если это неквалифицированный идентификатор, за которым следует <и <strong>поиск имени либо находит одну или несколько функций, либо ничего не находит .
Имя поиск в области видимости класса vec
найдите имя функции, за которым следует символ <
, так что это имя относится к шаблону.