考虑以下示例:
struct S {
template<typename T = void>
friend void foo(S) {
}
};
int main() {
S s;
foo(s); // (1)
foo<void>(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);
| ^~~~
但是,(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)
}
现在的问题是为什么foo(s)
有效。这是因为依赖于参数的查找。在没有嵌套名称说明符的函数调用中,还将搜索调用参数(和其他参数)类型的类和封闭的名称空间,以查找匹配的函数重载。对于依赖于参数的查找,friend
仅在类主体中声明的s是可见的。通过这种方式,foo(s)
可以找到呼叫的匹配功能。
foo<void>(s)
应工作方式相同,因为这个名字是不合格的,并s
是类型的S
,所以ADL应该再次找到朋友foo
里面S
。
但是,还有另一个问题要考虑。编译器读取时foo
,必须决定是否foo
可以是模板,因为它更改了<
following的解析foo
。
为了确定这一点,对进行了不合格的名称查找foo
。在C ++ 20之前,foo
仅当此查找通过该名称找到某种模板时,才被视为模板名称。但是在您的情况下,不合格的名称查找无法找到任何内容,因为foo
对于普通的不合格的名称查找来说,唯一的内容是不可见的。因此,foo
将不会被视为模板,foo<void>
也不会被解析为模板ID。
在C ++ 20中,更改了规则,并且如果不合格的名称查找通过该名称找到一个普通函数,或者根本找不到任何东西,则foo<void>
也将被视为模板ID。在这种情况下,将找到foo
该呼叫的以下ADL,并且该呼叫将成功。
因此,代码会工作在C ++ 20和PRE-C ++ 20,你实际上只需要声明任何由名模板foo
拿到foo<void>(s)
找到friendedfoo
通过ADL。例如:
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)
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句