模板函数重载相同的签名,为什么这个工作?

最小程序:

#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("%dn", foo<int, std::enable_if<true, int>>(x));

    return 0;
}

输出:

    1 

为什么这不会产生编译错误? 生成模板代码时,函数int foo(typename T::type search)int foo(S& search)是否具有相同的签名?

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

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

template<typename S, typename T>
void foo(S s) {
    printf("bn");
}

然而,这并不是唯一的区别,只有一个具有int签名,另一个是由第一个模板参数定义的。

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

template<typename S, typename T>
void foo(int s) {
    printf("bn");
}

编译器错误(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.

我正在使用类似于此的代码来处理我正在处理的项目,而且我担心这种语言有些微妙,我不理解这会在某些情况下导致某些未定义的行为。 我还应该提到它在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类型。


第一个功能被认为比第一个功能更专业。

功能

int foo(typename T::type)

可以匹配

template <typename S,typename T> int foo(S s)

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

int foo(S s)

将不匹配

template <typename S,typename T> int foo(typename T::type)

因为T不能被推断出来。

该逻辑放在14.5.5.2节的C ++ 03标准和14.5.6.2节的C ++ 11标准中。

这里有一个想法:要查看一个函数是否比另一个函数更专业化,可以为第一个函数的每个模板参数创建值,然后查看第二个函数是否可以匹配结果签名。 您还可以为第二个函数的模板参数创建值,并查看第一个函数是否与结果签名相匹配。 如果第二个函数可以匹配第一个函数,那么第二个函数不能比第一个函数更专门。 除此之外,如果第一个函数不能与第二个函数相匹配,那么第一个函数必须比第二个函数更专业。 你就是这样。


这是现象的进一步简化:

#include <stdio.h>

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

template<typename T>
void foo(T arg) {
    printf("bn");
}

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

    return 0;
}

模板参数可以通过直角括号显式传递,也可以通过推导来解决。 如果没有明确指定参数,则必须使用函数的参数来推导它。

所以,在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所说的那样专业化。

链接地址: http://www.djcxy.com/p/58711.html

上一篇: Template function overloading with identical signatures, why does this work?

下一篇: name optional in a typedef declaration?