C++ 候选函数集

C++ 候选函数集,c++,overloading,language-lawyer,C++,Overloading,Language Lawyer,假设我有以下功能: #include <iostream> class A{ }; class B{ }; void foo(A&& a){ std::cout << "A&&" << std::endl; }; void foo(A& a){ std::cout << "A&" << std::endl; }; void foo(B& b, B& bb){ std::c

假设我有以下功能:

#include <iostream>
class A{ };
class B{ };

void foo(A&& a){ std::cout << "A&&" << std::endl; };
void foo(A& a){ std::cout << "A&" << std::endl; };
void foo(B& b, B& bb){ std::cout << "B&, B&" << std::endl; };
void foo(B& b){ std::cout << "B&" << std::endl; };
void foo(const A& a){ std::cout << "const A&" << std::endl; };

A a;

int main()
{
    foo(a);
}
#包括
A类{};
B类{};
void foo(A&&A){std::cout来自[over.call.func]:

按照函数调用中名称查找的常规规则(3.4),在函数调用的上下文中查找名称。通过该查找找到的函数声明构成 候选函数

这只是非限定查找。因此候选函数是任何名为
foo
的函数。也就是说,所有这些函数:

void foo(A&& );
void foo(A& );
void foo(B& , B& );
void foo(B& );
void foo(const A& );
参数的数量是否匹配或任何转换是否可行都无关紧要——第一步只是名称查找。这就是为什么术语是候选函数。这些都是候选函数,我们还没有排除任何东西

除此之外,我们确定参数列表。这是您引用的第二个片段,其整体内容如下:

由于名称查找的规则,候选函数集完全由(1)个 对于非成员函数或(2)某些类T的全部成员函数。在情况(1)中,参数列表 与调用中的表达式列表相同


我们在这里是案例1。因此,在本例中,我们有5个候选函数和一个
a
的参数列表,所有列出的函数都将是候选函数。问题中引用的段落解释了跳过部分的原因:

在下面的函数调用上下文中查找该名称 函数调用中名称查找的常规规则。函数 通过该查找找到的声明构成候选集 功能

不必冒险进入名称查找的奇妙世界,您可以从其名称推断,如果找到
foo
函数中的一个,则所有函数都将被删除

更有趣的部分是在这个过程之后开始的,当编译器确定一组可行的函数时。为了更好地理解这个过程,我将介绍如何选择一个函数。我鼓励您阅读,看看我遗漏了什么。我使用了N4140

我们将从§13.3.2/2中的第一点开始:

如果列表中有m个参数,则所有具有m个参数的候选函数都是可行的

这排除了
void foo(B&B,B&bb)
。没有带省略号的函数或带默认参数的参数超过1个,因此我们将跳过这些函数并转到§13.3.2/3:

第二,F是一个可行的函数,每个函数都应该存在 参数转换该参数的隐式转换序列 到F的相应参数。如果该参数有参考 类型,隐式转换序列包括 绑定引用,以及左值引用 非常量不能绑定到右值,并且该值是右值引用 无法绑定到左值可能会影响函数的生存能力

没有从
a
B&
的隐式转换序列,
a&
不能绑定到
a
。这排除了
void foo(B&B)
void foo(a&&a)

现在我们要谈的是:

void foo(A& a)
void foo(const A& a)
我们有两个隐式转换序列:一个是将
a
转换为
a&
,另一个是将
a
转换为
const a&
。如果其中一个优于另一个(扰流板:是的),那么将选择该函数

这两者都属于§13.3.3.1.4,参考约束

当引用类型的参数直接(8.5.3)绑定到 参数表达式中,隐式转换序列是标识 转换,除非参数表达式的类型为 参数类型的派生类,在这种情况下,隐式 转换序列是派生到基的转换

标题至§8.5.3/4和§8.5.3/5:

给定类型“cv1 T1”和“cv2 T2”,“cv1 T1”与 “cv2 T2”如果T1与T2的类型相同,或者T1是T2的基类。 如果T1为,则“cv1 T1”与“cv2 T2”是参考兼容的 与T2和cv1相关的参考与相同的cv鉴定,或 cv资格比cv2更高。

类型“cv1 T1”的引用由类型为的表达式初始化 “cv2 T2”如下:(5.1)-如果参考是左值参考 初始化表达式(5.1.1)是一个左值(但不是 位字段),并且“cv1 T1”与“cv2 T2”是参考兼容的,

在所有情况下,最后一种情况除外(即,创建和初始化 临时(从初始值设定项表达式中),引用称为 直接绑定到初始值设定项表达式

由此,我们得出结论,两个隐式转换序列都是身份转换。最后,我们在§13.3.3.2/3中对其进行了排名:

给出了两个相同形式的隐式转换序列 不可区分的转换序列,除非满足以下条件之一 规则适用于:

  • 标准转换序列S1是一个更好的选择 转换顺序大于标准转换顺序S2,如果
    • S1和S2是引用绑定,以及 除顶级cv限定符外,引用的类型相同, 并且由S2初始化的引用所引用的类型更多 cv限定值大于由S1初始化引用的类型 参考
如果我们将
a
a&
取为S1,将
a
const a&
取为S2,我们会发现S2更符合cv,因此满足了该标准,S1是更好的转换序列


总之,
void foo(A&A)
将赢得重载解析并将被调用。

您想要候选函数还是可行函数?根据您引用的段落,它们都是候选函数。@chris否,I w