为什么在传递 long long 时调用具有两个 double 类型参数的重载函数?

Why is an overloaded function with two arguments of type double called when passing a long long?

我写了这两个重载:

int func(int, int) {
    return 1;
}

int func(double, double) {
    return 2;
}

当我用明显的两个调用方案调用它们时,即 func(1, 1)func(1.0, 1.0),分别调用第一个和第二个重载函数,当我尝试调用 func(1, 1.0) 它给了我一个错误,但是当我将 1 转换为 long long 时,我没有收到错误,第二个重载是调用的那个。

#include <iostream>
int main()
{
    std::cout << func(1, 1); // outputs 1.
    std::cout << func(1.0, 1.0); // outputs 2.
    // std::cout << func(1, 1.0); // erroneous.
    std::cout << func((long long)1, 1.0); // outputs 2.
}

为什么会这样?起初,我以为是因为一些提升,但我尝试了第三个带有两个浮点数的重载,但我无法像 func((int)1, 1.0f) 这样调用它来调用它。我不知道为什么不一样,我不知道为什么在传递 long long 时调用第二次重载。

选择调用重载集中的哪个函数(即overload resolution) depends (in part) on how many arguments of the function call must go through an implicit conversion,需要什么样的转换。

与您的示例相关的规则是:

For each pair of viable function F1 and F2, the implicit conversion sequences from the i-th argument to i-th parameter are ranked to determine which one is better.

F1 is determined to be a better function than F2 if implicit conversions for all arguments of F1 are not worse than the implicit conversions for all arguments of F2, and ... there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2.

因此给定重载集:

int func(int, int);        // #1
int func(double, double);  // #2 

让我们考虑以下调用:

func(1, 1);    // perfect match for #1, so #1 is chosen

func(1., 1.);  // perfect match for #2, so #2 is chosen

func(1., 1);   // could call #1 by converting 1st argument to int 
               // (floating-integral conversion)

               // could call #2 by converting 2nd argument to double 
               // (floating-integral conversion)

               // error: ambiguous (equal number of conversions needed for both #1 and #2)

func(1ll, 1.); // could call #1 by converting both arguments to ints
               // (integral conversion for 1st argument, floating-integral conversion for 2nd argument)

               // could call #2 by converting just 1st argument to double
               // (floating-integral conversion for 1st argument)

               // for the 2nd parameter, #2 is ranked as a better choice, 
               // since it has a better implicit conversion sequence for #2
               // and so #2 is chosen (even though both #1 and #2 are tied for the 1st argument)

现在让我们添加第三个重载:

int func(float, float);  // #3

现在当您拨打电话时:

func(1, 1.f);  // could call #1 by converting 2nd argument to int
               // (floating-integral conversion for 2nd argument)

               // could call #2 by converting 1st argument to double, and converting 2nd argument to double 
               // (floating-integral conversion for 1st argument, and floating-point promotion for 2nd argument)

               // could call #3 by converting 1st argument to float  
               // (floating-integral conversion for 1st argument)

               // error: ambiguous (equal number of conversions needed for #1, #2 and #3)

cppreference 中适用于您的情况的规则是:

For each pair of viable function F1 and F2, the implicit conversion sequences from the i-th argument to i-th parameter are ranked to determine which one is better.

F1 is determined to be a better function than F2 if implicit conversions for all arguments of F1 are not worse than the implicit conversions for all arguments of F2, and there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2.

These pair-wise comparisons are applied to all viable functions. If exactly one viable function is better than all others, overload resolution succeeds and this function is called. Otherwise, compilation fails.

另外,C++ Primer §6.6中有一些简单的词:

There is an overall best match if there is one and only one function for which:

  • The match for each argument is no worse than the match required by any other viable function.
  • There is at least one argument for which the match is better than the match provided by any other viable function.

If after looking at each argument there is no single function that is preferable, then the call is in error. The compiler will complain that the call is ambiguous.

using llong = long long;

void f(double, double);    // F1
void f(int, int);    // F2 

int main(void)
{
    f(double(0), double(0));
    // F1: double->double exact match; double->double exact match
    // F2: double->int conversion; double->int conversion

    // all argument of F1 is better conversion than F2, so F1 is best viable.

    f(int(0), short(0));
    // F1: int->double conversion; int->double conversion
    // F2: int->int exact match; short->int promotion

    // all argument of F2 is better conversion than F1, so F2 is best viable.

    f(short(0), char(0));
    // F1: short->double converstion; char->double converstion
    // F2: short->int promotion; char->int promotion

    // all argument of F2 is better conversion than F1, so F2 is best viable.

    f(float(0), float(0));
    //F1: float->double promotion; float->double promotion
    //F2: float->int conversion; float->int conversion

    // all argument of F1 is better conversion than F2, so F1 is best viable.

    f(double(0), int(0));
    //F1: double->double exact match; int->double converstion
    //F2 double->int converstion; int->int exact match

    // 1st argument in F1 is better conversion than that of F2, so F1 is best viable
    // 2nd argument of F2 is better conversion than that of F1, so F2 is best viable
    // F1 and F2 is better than each other, then the call is ambigious

    f(double(0), char(0));
    //F1: double->double exact match; char->double converstion
    //F2: double->int conversion; char->int promotion

    // 1st argument in F1 is better conversion than that of F2, so F1 is best viable
    // 2nd argument of F2 is better conversion than that of F1, so F2 is best viable
    // F1 and F2 is better than each other, then the call is ambigious

    f(llong(0), double(0));
    //F1: llong->double conversion; double->double exact match
    //F2: llong->int conversion; double->int conversion

    // 1st argument in F1 is equal-ranked with that of F2, 
    // 2nd argument of F1 is better conversion than that of F2, 
    // there's at least one argument of F1 is better conversion than that of F2
    // then F1 is best viable.

    f(long(0), bool(0));
    //F1: long->double conversion; bool->double conversion
    //F2: long->int conversion; bool->int promotion

    // 1st argument in F1 is equal-ranked with that of F2, 
    // 2nd argument of F1 is better conversion than that of F2, 
    // there's at least one argument of F1 is better conversion than that of F2
    // then F1 is best viable.

}