考虑以下示例:
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?
匿名用户
戈德博尔特的叮当声透露了一些信息:
<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.
在P084R0到达并修复它之前,这种语法似乎不是语言的一部分。
由于foo()
完全在S
结构内部实现,main()
中没有可见的模板foo
。因此,编译器不明白它需要做ADL,也无法找到foo()
。就像@0x499602D2说的。
一种解决方案是更新编译器,另一种解决方案是在S
之外实现foo
,并且可以选择添加转发声明以提供默认模板参数:
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);
}
如果您试图省略转发声明(a),您会发现由于各种原因,您无法将默认模板参数添加到(b)
,也无法将其添加到(c)
,因为它是对(b)
的重新声明,因此无法引入默认模板参数。
它是一个模板这一事实并不重要。只有通过ADL查找才能找到在声明位置定义的友元函数。当您使用模板参数时,编译器会尝试使用常规的非限定查找来查找名为foo的函数模板,但失败了foo(s)
查找具有关联命名空间(全局命名空间)的foo(并查找您定义的友元函数)。
类主体中的函数定义不会使函数在封闭的命名空间范围内可见,以进行常规的非限定名称查找(尽管它们被放置在此命名空间范围内)。
为了使其可见,您需要在命名空间范围内为模板添加声明(无论这发生在定义之前还是之后):
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)有效。这是因为依赖于参数的查找。在没有嵌套名称说明符的函数调用中,还将搜索调用参数类型的类和封闭名称空间(以及其他类型),以查找匹配的函数重载。对于依赖于参数的查找,仅在类主体中声明的s可见。这样就可以找到调用foo(s)的匹配函数。
<代码>foo
然而,还有另一个问题需要考虑。当编译器读取foo时,它必须决定foo是否可以是模板,因为它改变了对代码的解析
为了确定这一点,在foo
上进行了不合格的名称查找。在C 20之前,只有当此查找找到具有该名称的某种模板时,foo
才会被视为模板名称。但是不合格的名称查找在您的情况下找不到任何东西,因为唯一的foo
对于普通的不合格名称查找是不可见的。因此foo
将不被视为模板和foo
在C 20中,规则被更改了,如果非限定名称查找找到了一个使用该名称的普通函数,或者根本找不到任何函数,那么
因此,代码将与C 20和C 20之前的代码一样工作。实际上,您只需按名称声明任何模板,即可获得
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)
}
我正在学习一个视频教程,我想声明一个模板函数作为模板类的朋友。我不知道为什么代码会抛出错误。 编译器抛出错误。 错误: templates\u friends\u 38。cpp:在“void doSomething2(T)[T=int]”的实例化中:templates\u friends\u 38。cpp:40:19:此处需要templates\u friends\u 38。cpp:32:9:错误
我无法让它工作: 无法编译,错误消息如下: 无效使用temping-id'运算符*
我有一个模板,其中有一个friend函数的声明,在类之外,我有它的实现: 在其他文件中的某个地方将其命名为什么签名? 我尝试过: 但它说它无法解决这个问题。为什么?朋友成员应该这样看待,不是吗? 编辑: 这是在Troll.cpp在它的功能。 仍会喊出“未在此范围内声明”、“函数无法解析”、“符号无法解析”、“之前应为主表达式”、“之前应为主表达式”
我试图使乘法运算符成为名为TVector3的模板类的朋友。我读过,我可以在类声明中声明朋友函数之前,对其进行前向声明,但我这样做的尝试是徒劳的。我知道我可以简单地定义friend函数而不是声明它,但我希望它能与前向声明技术一起工作。 特别是,我试图为我的案例实施这个解决方案。我发现这篇文章也是David Rodriguez给出的解决方案(第三个版本),但我不知道我做错了什么。 我使用'g temp
我有以下代码: 这段代码会导致编译错误。使用< code>g -std=c 1z编译时,错误显示如下: 使用< code>clang -std=c 1z时,错误为: 我在MSYS2 MinGW-w64环境中运行这些。我的GCC版本是GCC 7.1.0,我的Clang版本是4.0.0;我在GCC和Clang中使用的标准库是与我的GCC编译器捆绑在一起的libstdc。 在我看来,对函数templat
我试图创建一个带有可选参数的模板函数,但我很难理解编译失败的原因。这是我的测试(设计)代码: 只有当我用这两个参数调用时,代码才会编译。如果我尝试只传递1个参数,编译将失败,出现以下错误: main.cpp:18:34:错误:调用'GetCountIf' std::cout<<“numelements=”< (我用的是C++14)