jac*_*k X 7 c++ language-lawyer
考虑这个例子
#include <iostream>
template<class T>
void fun(T&){ //#1
std::cout<<"selected\n";
}
template<class T>
void fun(T&&){} //#2
int main() {
void(*ptr)(int&) = &fun; //#3
}
Run Code Online (Sandbox Code Playgroud)
GCC 和 Clang 都报告错误,诊断为“模棱两可”。根据[temp.deduct.funcaddr#1],这两个函数模板在#3
. 因此,[over.over#5] 需要在这里申请
根据 [temp.func.order] 的偏序规则,如果该集合包含第二个函数模板特化,其函数模板比 F1 的函数模板更特化,则消除任何给定的函数模板特化 F1。
为了判断#1
和之间哪个更专业#2
,[temp.deduct.partial#3.3]适用于它们
用于确定排序的类型取决于完成偏序的上下文:
- [...]
- 在其他上下文中,使用函数模板的函数类型。
因此,用于参与偏序的 P 和 A应该分别是这两个函数模板的函数类型。取函数类型#2
为P,函数类型#1
为A ,根据[temp.deduct.type#10]推导P
出A
成功
如果 P 和 A 是在取函数模板地址 ([temp.deduct.funcaddr]) 或从函数声明 ([temp.deduct.decl]) 和 Pi 和 Ai 推导模板参数时源自推导的函数类型分别是 P 和 A 的顶级参数类型列表的参数,如果 Pi 是转发引用([temp.deduct.call])和 Ai 是左值引用,则调整 Pi 的类型,在这种情况下, Pi 更改为模板参数类型(即,T&& 更改为简单的 T)。
相反,由于我们不能T&
从推导出T&&
,因此函数模板 at#1
比函数模板 at 更专业#2
。因此,#2
应该从集合中消除 的特化。最终,该集合仅包含 的一个特化#1
。这里应该是明确的。为什么 GCC 和 Clang 说获取地址不明确?
根据您自己的分析,GCC 和 Clang 在重载解析中的屈服和模糊性是错误的。
这可以说与CWG 1164相关,尽管不是在函数调用的上下文中,但其意图应该与 CWG 1164 中的函数调用的情况类似[强调我的]:
1164.f(T&) 和 f(T&&) 的偏序
部分:13.10.3.2 [temp.deduct.call] 状态:C++11 提交者:美国 日期:2010-08-03
[在 2010 年 11 月的会议上投票加入 WP。]
N3092 comment US 77 以下示例不明确:
Run Code Online (Sandbox Code Playgroud)template<typename T> int f(T&); template<typename T> int f(T&&); int i; int j = f(i);
由于传递给右值引用参数的左值有特殊的推导规则,推导会为两个模板生成 f(int&),并且它们是无法区分的。
因为 f(T&) 接受 f(T&&) 所做的事情的严格子集,所以应该通过部分排序规则将其视为更专门化。
拟议决议(2010 年 8 月): [...]
您自己的分析得出了与 CWG 1164 一致的过载结果,但针对另一个上下文,该结果在 CWG 1164 中从未被解除,并且(可以说)不太常见。
我们可能会注意到,GCC 和 Clangs 都将 CWG 1164 的决议标记为?
/ Unknown
:
- GCC: GCC 中的 C++ 缺陷报告支持
- Clang 中的 C++ 缺陷报告支持
因此,它可能只是部分实现(OP 的示例更多是角落使用)情况优于 CWG 1164)。