重载解析算法中的歧义是如何确定的?

How is ambiguity determined in the overload resolution algorithm?

我正在尝试了解重载解析方法。

为什么会这样模棱两可:

void func(double, int, int, double) {}
void func(int, double, double, double) {}

void main()
{
    func(1, 2, 3, 4);
}

但这不是吗?

void func(int, int, int, double) {}
void func(int, double, double, double) {}

void main()
{
    func(1, 2, 3, 4);
}

在第一种情况下,有 2 次完全匹配的参数和 2 次转换对 1 次完全匹配和 3 次转换,在第二种情况下,有 3 次完全匹配和 1 次转换对 1 次完全匹配和 3 次转换。

那为什么一个有歧义,一个没有呢?这是什么逻辑?

重载解析规则仅定义所有匹配集的部分顺序 - 如果重载 F1 不是比 F2 更好的匹配,它并不意味着 F2F1 更匹配。确切的偏序可以被认为是比较 k 维度中的两个点,其中参数的数量是 k。让我们在 k-dim space - (x_1, x_2,..., x_k) < (y_1, y_2,..., y_k) if x_i <= y_i for all i and x_j < y_j for at least one j 中的点上定义此偏序。这正是标准定义的候选非模板函数的偏序。

让我们看看你的例子:

void func(double, int,    int,    double) {}
                  vvv     vvv       vvv
                 better  better    equal
void func(int,    double, double, double) {}
          vvv                       vvv
         better                    equal

所以这两个重载严格来说都不比另一个好。

在你的第二个例子中:

void func(int,   int,   int,   double) {}
          vvv    vvv    vvv     vvv
         equal  better better  equal
void func(int, double, double, double) {}
          vvv
         equal

现在,除了一个参数外,第一个重载比第二个好,而且永远不会比第二个差。因此,没有歧义 - 部分顺序确实声明第一个更好。

(以上描述未考虑函数模板,详情请见cppreference。)

标准 (§[over.match.best]/1) 中的措辞是:

[...] let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F.
[...] a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2)

在你的第一种情况下,这两个函数在第一次测试中失败。对于第一个参数,第一个函数(取 double)的转换顺序比第二个差。对于第二个参数,第二个函数的转换顺序比第一个差(同样,在一种情况下 int 必须提升为 double,而在另一种情况下则不需要)。

因此,两个函数都没有通过第一个规则,并且调用不明确。

在第二对函数之间,第一个函数的每个参数的转换至少与第二个函数的匹配参数一样好。然后我们继续第二条规则,发现至少有一个参数(事实上是两个)第一个函数比第二个函数具有更好的转换(身份而不是提升)。

因此,第一个功能比较匹配,将被选中。

歧义由排名决定:

  1. 精确匹配:不需要转换,左值到右值的转换,限定转换,class类型到相同class
  2. 的用户自定义转换
  3. 推广方式:积分推广、浮点推广
  4. 转换:整数转换、浮点数转换、浮点数转换、指针转换、指针到成员的转换、布尔值转换、派生class到其基数的用户定义转换

精确匹配胜过推广,胜过转化。

示例中:

void func(int, bool, float, int){cout << "int,bool,float,int" << endl;}
void func(int, bool, int, int){cout << "int,int,int,int" << endl;}

int main()
{
    func(1,1,3.4,4);
}

参数 1(1) 与两者完全匹配
参数 2(1) 与
完全匹配 参数 3(3.4) 可以转换为 float 和 int - 歧义 更好.
参数 4(4) 与

完全匹配

但如果我们这样做:func(1,1,3.4f,4);
(3.4f) 现在是完全匹配!
void func(int, bool, float, int)然后赢得战斗。