具有定义的友元函数 - 模板还是非模板?

Friend function with a definition - template or non-template?

假设我们有以下代码:

template<class T> struct S;
template<class T> void operator++(S<T>);

template<class T> struct S {
    friend void operator++(S);
};

template<class T>
void operator++(S<T>) {}

int main() {
    S<int> s;
    ++s;
}

这会编译但不会 link,因为 friend 声明引入了一个从未定义过的非模板 operator++

This FAQ answer 阅读(粗体 是我的):

The solution is to convince the compiler while it is examining the class body proper that the operator++ function is itself a template. There are several ways to do this;

第一种方式是在好友声明中加入<>,这里不考虑。第二个是 "to define the friend function within the class body":

template<class T> struct S {
    friend void operator++(S) { }
};

引用表明 void operator++(S) 现在是函数模板而不是非模板函数。是吗?

template<class T> struct S {
    friend void operator++(S s) { }
};

operator ++ 不再是模板。

对于更常规的功能(operator用法与功能略有不同),它可能允许推导:

template<class T> struct S {
    S(T t);
    friend void foo(S lhs, S rhs) { }
};

template <typename T>
void bar(S<T> s, T t)
{
    foo(s, t);   // would not work if foo was template, would require foo<T>(s, t);
    foo(s, {t}); // would not work if foo was template, would require foo<T>(s, {t});
}

它不是模板,因为它的声明不是模板的声明(即使它出现在模板声明本身内部)。

[temp.friend] (emphasis mine)

1 A friend of a class or class template can be a function template or class template, a specialization of a function template or class template, or a non-template function or class. For a friend function declaration that is not a template declaration:

  • if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a specialization of a function template, otherwise,

  • if the name of the friend is a qualified-id and a matching non-template function is found in the specified class or namespace, the friend declaration refers to that function, otherwise,

  • if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template ([temp.deduct.decl]), otherwise,

  • the name shall be an unqualified-id that declares (or redeclares) a non-template function.

[ Example:

template<class T> class task;
template<class T> task<T>* preempt(task<T>*);

template<class T> class task {
  friend void next_time();
  friend void process(task<T>*);
  friend task<T>* preempt<T>(task<T>*);
  template<class C> friend int func(C);

  friend class task<int>;
  template<class P> friend class frd;
};

Here, each specialization of the task class template has the function next_­time as a friend; because process does not have explicit template-arguments, each specialization of the task class template has an appropriately typed function process as a friend, and this friend is not a function template specialization; because the friend preempt has an explicit template-argument T, each specialization of the task class template has the appropriate specialization of the function template preempt as a friend; and each specialization of the task class template has all specializations of the function template func as friends. Similarly, each specialization of the task class template has the class template specialization task<int> as a friend, and has all specializations of the class template frd as friends.  — end example ]

虽然示例是非规范性的,但引用中的示例阐明了前面规范性文本的意图。由于友元运算符声明不是模板声明,因此适用粗体文本。因此它声明了一个非模板函数。