14

最小程序:

#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

模板< bool B,类 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。

4

2 回答 2

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 标准中进行了布局。

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

于 2012-12-20T06:17:40.363 回答
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;
}

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

因此,在 的情况下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

I see now that someone else has already answered (I'm bad at answering questions quickly), so I'm going to just wrap this up now and say that template 'a' is more specialized like Vaughn said.

于 2012-12-20T06:41:51.760 回答