C++ 为什么可以';我不能用显式模板参数调用模板友元函数吗?

C++ 为什么可以';我不能用显式模板参数调用模板友元函数吗?,c++,templates,friend,friend-function,C++,Templates,Friend,Friend Function,考虑以下示例: 结构{ 样板 富友友(S){ } }; int main(){ S S; 富(s);//(1) 富(s);//(2) } 我的GCC 9.2.0未能编译(2),出现以下错误: a.cpp: In function 'int main()': a.cpp:10:5: error: 'foo' was not declared in this scope 10 | foo<void>(s); | ^~~ a.cpp:10:9: erro

考虑以下示例:

结构{
样板
富友友(S){
}
};
int main(){
S S;
富(s);//(1)
富(s);//(2)
}
我的GCC 9.2.0未能编译
(2)
,出现以下错误:

a.cpp: In function 'int main()':
a.cpp:10:5: error: 'foo' was not declared in this scope
   10 |     foo<void>(s);
      |     ^~~
a.cpp:10:9: error: expected primary-expression before 'void'
   10 |     foo<void>(s);
      |         ^~~~
a.cpp:在函数“int main()”中:
a、 cpp:10:5:错误:未在此作用域中声明“foo”
10 |富(s);;
|     ^~~
a、 cpp:10:9:错误:应在“void”之前使用主表达式
10 |富(s);;
|         ^~~~

但是,
(1)
工作正常。为什么会这样?如何使用显式模板参数调用
foo

它是模板这一事实并不重要。在声明友元函数的位置定义的友元函数只能通过查找找到。当您使用模板参数时,编译器会尝试使用正常的非限定查找来查找名为
foo
的函数模板,但失败
foo(s)
查找
foo
s
(全局名称空间)相关联的名称空间,并找到您定义的友元函数。

Clang能够提供一些信息:

<source>:9:5: warning: use of function template name with no prior declaration in function call with explicit template arguments is a C++20 extension [-Wc++20-extensions]

    foo<void>(s);
    ^

1 warning generated.

如果您试图省略前向声明
(a)
,您会发现您无法将默认模板参数添加到
(b)
,因为它是
(b)的重新声明,您无法将其添加到
(c)
,因此无法引入默认模板参数。

类主体中的friend
函数定义不会使
friend
函数在封闭的命名空间范围内可见,以进行常规的非限定名称查找(尽管它们被放置在此命名空间范围内)

为了使其可见,您需要在名称空间范围中为模板添加一个声明(无论这是在定义之前还是之后发生):


有趣的我不知道规则变了。:)
struct S;

template<typename T = void>
void foo(S); // (a)

struct S {
    template<typename T>
    friend void foo(S); // (b)
};

template<typename T>
void foo(S) { // (c)
    // Needs S to be complete.
}

int main() {
    S s;
    foo(s);
    foo<void>(s);
}
struct S {
    template<typename T = void>
    friend void foo(S) {
    }
};

template<typename T>
void foo(S);

int main() {
    S s;
    foo(s); // (1)
    foo<void>(s); // (2)
}
struct S {
  template <typename T = void>
  friend void foo(S) {}
};

template<int>
void foo();

int main() {
  S s;
  foo(s);        // (1)
  foo<void>(s);  // (2)
}