13

考虑这段代码:

#include <iostream>

//Number1
template<typename T1, typename T2>
auto max (T1 a, T2 b)
{
    std::cout << "auto max(T1 a, T2 b)" <<std::endl;
    return  b < a ? a : b;
}

//Number2
template<typename RT, typename T1, typename T2>
RT max (T1 a, T2 b)
{
    std::cout << "RT max(T1 a, T2 b)" << std::endl;
    return  b < a ? a : b;
}


int main()
{
    auto a = ::max(4, 7.2);         //Select Number1

    auto b = ::max<double>(4, 7.4); //Select Number2

    auto c = ::max<int>(7, 4.);     //Compile-time error overload ambiguous

    auto c = ::max<double>(7, 4.); //Select Number2

}

auto c = ::max<int>(7, 4.);:由于重载歧义,此行无法编译,并显示以下消息:

maxdefault4.cpp:9:27: error: call of overloaded 'max(int, double)' is ambiguous
  auto c = ::max<int>(7, 4.);
                           ^
maxdefault4.cpp:9:27: note: candidates are:
In file included from maxdefault4.cpp:1:0:
maxdefault4.hpp:4:6: note: auto max(T1, T2) [with T1 = int; T2 = double]
 auto max (T1 a, T2 b)
      ^
maxdefault4.hpp:11:4: note: RT max(T1, T2) [with RT = int; T1 = int; T2 = double]
 RT max (T1 a, T2 b)
    ^

虽然以下代码: àuto c = ::max<double>(7, 4.)成功,但为什么我们没有相同的错误消息,说明调用与失败max<double>的方式相同max<int>

为什么double没有问题?

我在“C++ 模板,完整指南”一书中读到,模板参数推导没有考虑返回类型,那么为什么max<int>是模棱两可而不是max<double>呢?

参数推导中是否真的没有考虑模板函数的返回类型?

4

3 回答 3

15

模板参数推导不考虑返回类型,

是的。模板参数推导是基于函数参数执行的。

那么为什么max<int>是模棱两可而不是max<double>呢?

给定::max<int>(7, 4.),对于第一个重载,第一个模板参数T1指定为int,并从第二个函数参数T2推导出来,那么实例化将是。对于第二次重载,第一个模板参数指定为,推导为from ,推导为from ,则实例化为。重载解析也不考虑返回类型,这两个重载都是完全匹配的,然后是模棱两可的。double4.double max(int, double)RTintT1int7T2double4.int max(int, double)

Given ::max<double>(7, 4.), for the 1st overload, the 1st template parameter T1 is specified as double, and T2 is deduced as double from 4., so the instantiation would be double max(double, double). For the 2nd overload, the 1st template parameter RT is specified as double, T1 is deduced as int from 7, T2 is deduced as double from 4., then the instantiation would be double max(int, double). Then the 2nd overload wins in overload resolution because it's an exact match, the 1st one requires the implicit conversion from int to double for the 1st argument 7.

于 2019-09-04T13:39:49.373 回答
3

For each of your function calls the compiler has 2 functions to choose from and chooses the best one. Unknown template parameters are deduced from the arguments apart from RT which must be explicitly specified and can't be deduced.

auto a = ::max(4, 7.2);

As RT is not specified and can't be deduced, the second overload is not usable so is ignored. The first is chosen and the types are deduced as int and double.

auto b = ::max<double>(4, 7.4);

RT is now specified so the compiler can choose to either use max<double,int,double> or max<double, double>, the argument types for the 3 template parameter version match the function arguments exactly whereas the 2 template parameter version would require a cast from int to double so the 3 parameter overload is chosen.

auto c = ::max<int>(7, 4.);

RT is now specified so the compiler can choose to either use max<int,int,double> or max<int, double>, the argument types both functions are now the same so the compiler can't choose between them.

于 2019-09-04T13:43:39.637 回答
2

Let's look at what specifying double as an argument does for the compiler during overload resolution.

For the "Number1" max template, it specifies that the first argument must be of type double. When attempting to do the template match, the compiler deduces that the second argument is of type double. So the resultant signature is auto max(double, double). That's a match, though it involves casting the first argument from int to double.

For the "Number2" max template, it specifies that the return type is double. The argument types are deduced. So the resultant signature is double max(int, double). That's an exact match, removing any ambiguity.

Now let's look at specifying int. Now the two signatures are auto max(int, double) and double max(int, double). As you can see, there's no difference which is relevant to overload resolution, resulting in the ambiguity.

Essentially, by passing in double, you've poisoned one of the overloads by forcing an unnecessary conversion; the other overload thereby gets to dominate. Passing in int, in contrast, does not further constrain either overload's ability to be a perfect match.

于 2019-09-04T13:43:00.217 回答