最小程序:
#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");
}
编译器错误(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:
模板&LT; bool B,T级= void&gt; struct enable_if;
如果B为true,则std :: enable_if具有公共成员typedef类型,等于 到T;否则,没有成员typedef。
enable_if是一个结构。基本上,如果在enable_if的第一个模板参数中计算的表达式为真(并且在上面的示例的情况下,它是),那么将有一个与第二个类型相同的公共成员type
模板参数。
在enable_if<true, int>
的情况下,enable_if :: type的类型为int。
答案 0 :(得分:8)
第一个功能被认为比第一个功能更专业。
功能
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标准中列出。
这是一个想法:要查看一个函数是否比另一个函数更专业,您可以为第一个函数的每个模板参数创建值,然后查看第二个函数是否可以匹配生成的签名。您还可以为第二个函数的模板参数创建值,并查看第一个函数是否与生成的签名匹配。如果第二个函数可以匹配第一个函数,则第二个函数不能比第一个函数更专业。除此之外,如果第一个函数与第二个函数不匹配,那么第一个函数必须比第二个函数更专业。你就是这种情况。
答案 1 :(得分:4)
这是现象的进一步简化:
#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'更喜欢?这里的关键是“部分订购”:
我现在看到其他人已经回答了(我很难快速回答问题),所以我现在就把这个问题包起来,并说模板'a'比Vaughn说的更专业。