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

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

本文关键字:友元 函数 是非 定义      更新时间:2023-10-16

假设我们有以下代码:

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;
}

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

这个常见问题解答如下(bold是我的(:

解决方案是在检查类主体时说服编译器operator++函数本身就是一个模板。有几种方法可以做到这一点;

第一种方法是将<>添加到朋友声明中,我在这里不考虑它。第二个是"定义类主体内的友元函数":

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

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

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

[临时好友](强调矿(

1类或类模板的朋友可以是函数模板或类模板,函数模板或类的专门化模板或非模板函数或类对于好友功能不是模板声明的声明:

  • 如果友元的名称是一个合格或不合格的模板id,则友元声明指的是函数的特殊化模板,否则,

  • 如果朋友的名称是合格的id并且在指定的类或命名空间中找到匹配的非模板函数,友元声明指的是该函数,否则为

  • 如果好友的名称是合格的id,并且在指定的类或命名空间中找到了匹配的函数模板,则该好友声明指的是该函数的推导特殊化template([temp.decture.dell](,否则,

  • 名称应为声明(或重新声明(非模板函数的非限定id

[示例:

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;
};

这里,任务类模板的每个专门化都具有以下功能next_­time作为朋友因为process没有显式模板参数,任务类模板的每个专门化都有适当类型的函数process作为朋友,并且该朋友不是函数模板专用化;因为朋友preempt有一个显式模板参数T,每个专门化task类模板的功能模板CCD_ 12作为好友;以及task类模板具有函数的所有特殊化模板CCD_ 14作为好友。类似地task类模板具有类模板专业化task<int>作为朋友,拥有班上所有专业模板CCD_ 17作为好友。—结束示例]

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

使用

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});
}