具有相同签名的模板函数重载,为什么这有效?

vmr*_*rob 14 c++ templates overloading

最小程序:

#include <stdio.h>

#include <type_traits>

template<typename S, typename T>
int foo(typename T::type s) {
    return 1;
}

template<typename S, typename T>
int foo(S s) {
    return 2;
}

int main(int argc, char* argv[]) {
    int x = 3;
    printf("%d\n", foo<int, std::enable_if<true, int>>(x));

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

输出:

    1 
Run Code Online (Sandbox Code Playgroud)

为什么不给出编译错误?生成模板代码时,功能不会int foo(typename T::type search)int foo(S& search)签名相同吗?

如果您稍微更改模板函数签名,它仍然有效(正如我所期望的上面的示例):

template<typename S, typename T>
void foo(typename T::type s) {
    printf("a\n");
}

template<typename S, typename T>
void foo(S s) {
    printf("b\n");
}
Run Code Online (Sandbox Code Playgroud)

然而,这没有,但唯一的区别是一个具有int签名而另一个由第一个模板参数定义.

template<typename S, typename T>
void foo(typename T::type s) {
    printf("a\n");
}

template<typename S, typename T>
void foo(int s) {
    printf("b\n");
}
Run Code Online (Sandbox Code Playgroud)

编译器错误(Clang):

test.cpp:26:2: error: call to 'foo' is ambiguous
foo<std::enable_if<true, int>>(3);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:16:6: note: candidate function [with T = std::__1::enable_if<true, int>]
void foo(typename T::type s) {
        ^
test.cpp:21:6: note: candidate function [with T = std::__1::enable_if<true, int>]
void foo(int s) {
        ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

我正在使用与我正在进行的项目类似的代码,我担心我不理解的语言会在某些情况下导致一些未定义的行为.我还要提一下,它确实在Clang和VS11上编译,所以我不认为它只是一个编译器错误.


编辑:更正了第二种情况(拼写错误); 来自Clang的错误消息.

编辑#2:对于那些询问T :: type意味着什么的人.

来自http://en.cppreference.com/w/cpp/types/enable_if:

template <bool B,class T = void> struct enable_if;

如果B为真,则std :: enable_if具有公共成员typedef类型,等于T; 否则,没有成员typedef.

enable_if是一个结构.基本上,如果在enable_if的第一个模板参数中计算的表达式为真(并且在上面的示例的情况下,它是),那么将有一个type与第二个模板参数具有相同类型的公共成员.

在这种情况下enable_if<true, int>,enable_if :: type的类型为int.

Vau*_*ato 8

第一个函数被认为比第一个函数更专业.

功能

int foo(typename T::type)
Run Code Online (Sandbox Code Playgroud)

可以匹配

template <typename S,typename T> int foo(S s)
Run Code Online (Sandbox Code Playgroud)

通过使用T :: type作为参数S的值,但是

int foo(S s)
Run Code Online (Sandbox Code Playgroud)

不会匹配

template <typename S,typename T> int foo(typename T::type)
Run Code Online (Sandbox Code Playgroud)

因为T不能推断出来.

逻辑在第14.5.5.2节中的C++ 03标准和第14.5.6.2节中的C++ 11标准中列出.

这是一个想法:要查看一个函数是否比另一个函数更专业,您可以为第一个函数的每个模板参数创建值,然后查看第二个函数是否可以匹配生成的签名.您还可以为第二个函数的模板参数创建值,并查看第一个函数是否与生成的签名匹配.如果第二个函数可以匹配第一个函数,则第二个函数不能比第一个函数更专业.除此之外,如果第一个函数与第二个函数不匹配,那么第一个函数必须比第二个函数更专业.你就是这种情况.


dax*_*tro 5

这是对现象的进一步简化:

#include <stdio.h>

template<typename T>
void foo(int arg) {
    printf("a\n");
}

template<typename T>
void foo(T arg) {
    printf("b\n");
}

int main(int argc, char* argv[]) {
    foo<int>(3);   // prints "a"
    foo(3);        // prints "b"

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

模板参数既可以通过尖括号显式传递,也可以通过推导解析。如果未明确指定参数,则必须使用函数的参数推导出该参数。

因此,在 的情况下foo(3),模板 'a' 将不起作用,因为参数 T 未明确指定且无法推导出。在 的情况下foo<int>(3),两个模板都可以工作。事实上,如果你注释掉模板“a”,调用foo<int>(3)将打印“b”。所以问题是,为什么首选模板“a”?这里的关键是“部分排序”:

http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fpartial_ordering_funct_templ.htm

我现在看到其他人已经回答了(我不擅长快速回答问题),所以我现在要总结一下,并说模板 'a' 更专业,就像 Vaughn 所说的那样。