yep*_*ons 4 c++ templates friend friend-function
考虑以下示例:
struct S {
template<typename T = void>
friend void foo(S) {
}
};
int main() {
S s;
foo(s); // (1)
foo<void>(s); // (2)
}
Run Code Online (Sandbox Code Playgroud)
我的 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);
| ^~~~
Run Code Online (Sandbox Code Playgroud)
不过,(1)效果很好。为什么是这样?如何foo使用显式模板参数进行调用?
friend类主体中的函数定义不会使friend函数在封闭的命名空间范围中对通常的非限定名称查找可见(尽管它们被放置在此命名空间范围中)。
为了使其可见,您需要在命名空间范围中添加模板的声明(无论这发生在定义之前还是之后):
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)
}
Run Code Online (Sandbox Code Playgroud)
现在的问题是为什么foo(s)有效。这是因为依赖于参数的查找。在没有嵌套名称说明符的函数调用中,还会搜索调用参数(和其他)类型的类和封闭名称空间以查找匹配的函数重载。对于friend仅在类主体中声明的参数相关查找是可见的。这样foo(s)就找到了与调用匹配的函数。
foo<void>(s)应该以同样的方式工作,因为名称是非限定的并且s是类型,所以 ADL 应该再次找到里面的S朋友。fooS
然而,还有另一个问题需要考虑。当编译器读取时,foo它必须决定是否foo可以是模板,因为它改变了<以下foo.
为了决定这一点,在 上完成了非限定名称查找foo。在 C++20 之前,foo仅当此查找找到某种具有该名称的模板时,才会被视为模板名称。但是,非限定名称查找在您的情况下找不到任何内容,因为唯一的foo对普通非限定名称查找不可见。因此foo不会被视为模板,也foo<void>不会被解析为 template-id。
在 C++20 中,规则已更改,如果非限定名称查找找到该名称的普通函数或根本找不到任何内容foo<void>,则也将被视为模板 ID。在这种情况下,将找到该呼叫的以下 ADL foo,并且呼叫将成功。
因此,代码将像 C++20 和 C++20 之前的版本一样工作,实际上您只需要按名称声明任何foo模板即可foo<void>(s)找到 ADL 的友元foo。例如:
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)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8088 次 |
| 最近记录: |