2012-12-20 12 views
12

最小程序:模板函数使用相同的签名重载,为什么这会起作用?

#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; 
} 

输出:

1 

为什么不给这个编译错误?当生成模板代码时,函数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"); 
} 

但这并不,但唯一的区别是一个具有一个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"); 
} 

编译器错误(锵):

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来自:

模板<布尔B,类T =空隙> 结构enable_if;

如果B是真实的,性病:: enable_if有一个公共的typedef构件类型,等于 至T;否则,没有成员typedef。

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

enable_if<true, int>的情况下,enable_if ::类型具有int类型。

+1

这不奇怪,你最后一个案例不编译。当函数仅接收一个函数时,您正在使用两个模板参数。 –

+0

谢谢,我想我在那里复制了错误的案例。固定。 – vmrob

+0

我是C++ noob,所以不要苛刻我......第一个例子不工作,因为你重写第一个方法吗?我很想知道这个答案。我打算明年学习C++。 –

回答

7

第一个函数被认为比第一个函数更专门。

功能

int foo(typename T::type) 

可以通过使用T ::类型作为参数S的值相匹配

template <typename S,typename T> int foo(S 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标准中有详细说明。

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

2

这里的现象甚至进一步简化:

#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; 
} 

模板参数可以通过显式地尖括号被传递或者他们可以通过扣来解决。如果没有明确指定参数,则必须使用函数的参数来推导它。

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

相关问题