3

这是对这个问题的跟进。最初的案例是另外一回事,但在我写一个糟糕的答案和 OP 澄清的过程中,结果证明我们可能需要语言律师的帮助来了解发生了什么。

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

//: 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 类模板时,都会创建一个新的普通函数重载,它接受当前 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是普通功能吗?它仅在调用时实例化?为什么,当它是一个普通函数时?bar关于何时实例化实际发生了什么foo<int>(作者称其为“创建了一个新的、普通的函数重载”,不确定这应该是什么意思)?

对不起,这太令人费解了。请不要错过这个language-lawyer标签,我想知道为什么/标准的哪些部分使它如此,而不仅仅是什么。

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

4

2 回答 2

1

[temp.inst]/2类模板特化的隐式实例化会导致声明的隐式实例化,但不会导致类的定义、默认参数或noexcept 说明符的隐式实例化......朋友......

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

于 2021-04-29T02:42:36.777 回答
1

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

于 2021-04-29T04:11:13.093 回答