编译器如何决定调用哪个函数模板?

Rya*_*yan 2 c++ templates

编译器在重载两个函数模板时如何决定调用哪个函数:

#include <iostream>
#include <typeinfo>

#ifndef B1
template <typename T1, typename T2> 
auto max(T1 a, T2 b) {
    std::cout << "auto version called" << std::endl;
    return b < a ? a : b;
}
#endif

#ifndef B2
template <typename RT, typename T1, typename T2> 
RT max(T1 a, T2 b) {
    std::cout << "RT version called" << std::endl;
    return b < a ? a : b;
}
#endif

template <typename T>
void print(T t) {
    std::cout << typeid(t).name() << std::endl;
}

int main() {
    auto b = ::max<long double>(4, 7.2);
    print(b);

    auto c = ::max<int>(4, 7.2);
    print(c);

    auto d = ::max<double>(4, 7.2);
    print(d);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我用命令构建了代码:

$ g++ -o m1 ./maxdefault4.cpp  -std=c++14  -DB1
$ g++ -o m2 ./maxdefault4.cpp  -std=c++14  -DB2
Run Code Online (Sandbox Code Playgroud)

这意味着两个函数模板都可以匹配,但为什么没有引起歧义错误。如果使用命令构建:

$ g++ -o m ./maxdefault4.cpp  -std=c++14 
Run Code Online (Sandbox Code Playgroud)

编译器向我展示了错误:

error: call to 'max' is ambiguous
    auto c = ::max<int>(4, 7.2);
Run Code Online (Sandbox Code Playgroud)

cig*_*ien 5

我用命令构建了代码:

$ g++ -o m1 ./maxdefault4.cpp -std=c++14 -DB1

$ g++ -o m2 ./maxdefault4.cpp -std=c++14 -DB2

这意味着两个函数模板都可以匹配,但为什么没有引起歧义错误。

这是不正确的:当您通过-DB1, or 时-DB2,您只编译了一个模板,因此没有任何歧义的问题,并且编译它并不奇怪。

如果使用命令构建:

$ g++ -o m ./maxdefault4.cpp -std=c++14

编译器向我显示错误:...

当您编译两个模板时会发生这种情况,并且更有趣,因为有些调用会编译,而其他调用则不会。

要了解发生了什么,让我们看看当您拥有以下模板时会发生什么:

template <typename T1, typename T2> 
auto max(T1 a, T2 b);                // #1

template <typename RT, typename T1, typename T2> 
RT max(T1 a, T2 b);                  // #2
Run Code Online (Sandbox Code Playgroud)

然后你打电话:

max<double>(4, 7.2);
Run Code Online (Sandbox Code Playgroud)

在这里,编译器将替换提供的参数double,并从参数中推导出剩余的模板参数。

这导致生成:

template<>
double max<double, double>(double a, double b);  // #1' from #1
// T1 = double (explicitly specified for 1st template parameter)
// T2 = double (deduced from 2nd function argument)

template<>
double max<double, int, double>(int a, double b); // #2' from #2
// RT = double (explicitly specified for 1st template parameter)
// T1 = int (deduced from 1st function argument)
// T2 = double (deduced from 2nd function argument)
Run Code Online (Sandbox Code Playgroud)

现在使用重载解析来确定#2'参数4, 和的更好匹配7.2,因此#2'被调用。

同样,对于这个调用:

max<int>(4, 7.2);
Run Code Online (Sandbox Code Playgroud)

编译器将替换提供的参数int,并从参数中推导出剩余的模板参数。

这导致生成:

template<>
double max<int, double>(int a, double b);  // #1' from #1
// T1 = int (explicitly specified for 1st template parameter)
// T2 = double (deduced from 2nd function argument)

template<>
int max<int, int, double>(int a, double b); // #2' from #2
// RT = int (explicitly specified for 1st template parameter)
// T1 = int (deduced from 1st function argument)
// T2 = double (deduced from 2nd function argument)
Run Code Online (Sandbox Code Playgroud)

现在使用了重载解析,但是#1', 和之间存在歧义#2',因为两者都不比另一个更好(请注意,返回类型在重载解析中不起作用)。这会导致您观察到的错误。