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

C++ 模板中定义的友元函数的实例化,c++,templates,language-lawyer,friend,template-instantiation,C++,Templates,Language Lawyer,Friend,Template Instantiation,这是我们的后续行动。最初的案例是另一回事,但在我写了一个糟糕的答案和解释的过程中,我们可能需要语言律师的帮助来理解正在发生的事情 在C++中的实践——编程2卷,可以找到下面的例子(矿山,在线): 普通函数的实例化?只有在调用函数时才会失败?这是怎么回事 bar真的是一个普通的功能吗?它仅在调用时实例化?为什么,当它是一个普通函数时?当foo被实例化时,bar实际发生了什么(作者称之为“创建了一个新的、普通的函数重载”,不确定这意味着什么) 为这么多人感到抱歉,这太令人费解了。请不要错过语言律师标

这是我们的后续行动。最初的案例是另一回事,但在我写了一个糟糕的答案和解释的过程中,我们可能需要语言律师的帮助来理解正在发生的事情

<>在C++中的实践——编程2卷,可以找到下面的例子(矿山,在线):

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

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

为这么多人感到抱歉,这太令人费解了。请不要错过
语言律师
标签,我想知道标准中为什么/哪些部分这么做,而不仅仅是什么

PS:为了确保我再次检查,并且在未调用
bar
时,三个常见的嫌疑犯都编译了示例,没有重大投诉:

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

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


像这样的结构是模板的一部分,但其本身不是模板,因此称为模板化的,因为它们仍然受许多相同规则的约束(特别是在类模板的方法和友元分别实例化的情况下,赋予每个类模板自己的“实例化状态”)。在这种情况下,标准本身一直在缓慢地使用更精确的语言,部分原因是constexpr if引入了模板化的语句(因为它们必须单独实例化,以便只允许对一个分支进行实例化),即使没有语句模板。(对于这些结构,可能对进一步研究有用的一个较老的术语是“templaoids”。

AFAIK,类模板中声明的函数的定义(朋友也不例外)只有在使用ODR时才真正实例化,否则只实例化声明。这就是为什么您可以使用非默认可构造类型实例化
std::map
,只要您不使用它的
操作符[]
@Meowmere听起来很合理,尽管这样编译时也应该不会出错(不同的是现在
bar
不依赖于
T
),但现在clang在实例化
foo
时已经出错了。好吧,同时gcc和msvc接受它,也许这将是下一个后续行动。到目前为止,我虽然理解了
map::operator[]
的情况,它是类模板的一个成员,但这里的
bar
不是成员。这似乎是相关的。在C++17中添加的措辞表明,
bar
的定义在需要时才被实例化。顺便说一句,你评论中的内容与朋友无关。只是gcc在实例化成员之前不会解析模板成员函数的定义。我不确定这两种方法是否都是错误的。@Hmm。有一些规则是关于模板没有有效的专门化(标准的意思是实例化),如果;国家发改委。但如果它不是一个模板。。。那么,0也是一个模板的方法。不管怎样,有不到一打如果;ndr在标准中,看看这个,它可能会应用。这听起来像是一个很好的例子,说明了概念可以如何帮助:现在我确信,当他们写“创建了一个新的、普通的函数重载”时,这是一个口语。再加上另一个答案,事情现在清楚多了。从某种意义上说,我是以其他人的名义写这个问题的,我希望他们的疑虑也能得到澄清。你能告诉我引用的是哪个版本的标准吗?@maximust_prime_是_463035818这是C++17。C++20在[temp.inst]/(3.1)和[temp.inst]/5中有类似的语言
//: 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();
} ///:~
template <typename T>
struct foo {
    friend void bar(foo x){
        x = "123";
    }
};

int main() {
    foo<int> x;
    bar(x);
}
<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>&&'