在取地址的上下文中,哪个是最专业的函数模板?

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]推导PA成功

如果 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 说获取地址不明确?

dfr*_*fri 4

根据您自己的分析,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 以下示例不明确:

template<typename T> int f(T&);
template<typename T> int f(T&&);
int i;
int j = f(i);
Run Code Online (Sandbox Code Playgroud)

由于传递给右值引用参数的左值有特殊的推导规则,推导会为两个模板生成 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)。