模板中定义的友元函数的实例化

时间:2021-04-28 20:35:34

标签: c++ templates language-lawyer friend template-instantiation

这是 this question 的后续。最初的案例是别的,但在我写一个糟糕的答案和 OP 澄清的过程中,结果证明我们可能需要语言律师的帮助来了解发生了什么。

在 Thinking in C++ - Practical Programming Vol 2 中可以找到以下示例(意向我的,在线 here):

<块引用>
//: C05:FriendScope3.cpp {-bor}
// Microsoft: use the -Za (ANSI-compliant) option
#include <iostream>
using namespace std;
 
template<class T> class Friendly {
    T t;
public:
    Friendly(const T& theT) : t(theT) {}
    friend void f(const Friendly<T>& fo) {
        cout << fo.t << endl;
    }
    void g() { f(*this); }
};
 
void h() {
    f(Friendly<int>(1));
}
 
int main() {
    h();
    Friendly<int>(2).g();
} ///:~

他们继续解释(强调我的):

<块引用>

这个和前面的例子有一个重要的区别:f在这里不是模板,而是一个普通的函数。 (请记住,之前需要尖括号来暗示 f( ) 是模板。)每次实例化 Friendly 类模板时,都会创建一个新的普通函数重载,它接受当前的参数友好的专业化。这就是丹萨克斯所说的结交新朋友。 [68] 这是为模板定义友元函数最方便的方式。

到目前为止一切顺利。当你考虑这个例子时,令人费解的部分是“f在这里不是模板,而是一个普通函数”+“每次实例化Friendly类模板时,都会创建一个新的普通函数重载”:

template <typename T>
struct foo {
    friend void bar(foo x){
        x = "123";
    }
};

int main() {
    foo<int> x;
    bar(x);
}

实例化 foo<int> 不会导致编译器错误!仅调用 bar(x) 原因 (gcc 10.2):

<source>: In instantiation of 'void bar(foo<int>)':
<source>:10:10:   required from here
<source>:4:11: error: no match for 'operator=' (operand types are 'foo<int>' and 'const char [4]')
    4 |         x = "123";
      |         ~~^~~~~~~
<source>:2:8: note: candidate: 'constexpr foo<int>& foo<int>::operator=(const foo<int>&)'
    2 | struct foo {
      |        ^~~
<source>:2:8: note:   no known conversion for argument 1 from 'const char [4]' to 'const foo<int>&'
<source>:2:8: note: candidate: 'constexpr foo<int>& foo<int>::operator=(foo<int>&&)'
<source>:2:8: note:   no known conversion for argument 1 from 'const char [4]' to 'foo<int>&&'

普通函数的实例化?只有在调用函数时才会失败?这是怎么回事?

bar 真的是一个普通的函数吗?它仅在调用时实例化?为什么,当它是一个普通的函数?实例化 barfoo<int> 实际发生了什么(作者称其为“创建了一个新的、普通的函数重载”,不确定这意味着什么)?

抱歉有很多?s,太令人费解了。并且请不要错过 language-lawyer 标签,我想知道为什么/标准的哪些部分使它如此,而不仅仅是什么。

PS:为了确保我再次检查,当没有调用 bar 时,三个常见的嫌疑人都编译了这个例子,没有大的抱怨:https://godbolt.org/z/Wcsbc5qjv

2 个答案:

答案 0 :(得分:1)

<块引用>

[temp.inst]/2 类模板特化的隐式实例化导致声明的隐式实例化,而不是定义、默认参数或 noexcept-specifiers< /em> 班级...朋友...

[temp.inst]/4 ... 声明从友元函数定义实例化的函数在需要函数定义存在的上下文中被引用时隐式实例化。 .

答案 1 :(得分:1)

这样的结构是模板的一部分但本身不是模板的一部分被称为模板化,因为它们仍然服从许多相同的规则(特别是在类模板的方法和朋友被单独实例化的情况下) ,给每个人自己的“实例化状态”)。对于这种情况,标准本身已经慢慢地使用了更精确的语言,部分原因是 constexpr-if 引入了模板化的语句(因为它们必须单独实例化,以便仅对一个分支这样做) 即使没有语句模板。 (这些结构可能对进一步研究有用的旧术语是“temploids”。)