【问题标题】:Template function overloading with identical signatures, why does this work?具有相同签名的模板函数重载,为什么会这样?
【发布时间】:2012-12-07 13:48:11
【问题描述】:

最小程序:

#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&amp; 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

模板 struct enable_if;

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

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

对于enable_if&lt;true, int&gt;,enable_if::type 的类型是int。

【问题讨论】:

  • 最后一个案例没有编译并不奇怪。当函数只接收一个时,您正在使用两个模板参数。
  • 谢谢,我想我在那里复制了错误的案例。固定。
  • 我是一个 C++ 菜鸟,所以不要苛责我...第一个示例不是因为您覆盖了第一个方法而起作用吗?我很想知道这个问题的答案。我计划明年学习 C++。
  • 有人能解释一下“typename T::type s”是什么意思吗?
  • 很好奇。为@Johannes 呐喊

标签: c++ templates overloading


【解决方案1】:

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

功能

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 不能推导出来。

C++03 标准的第 14.5.5.2 节和 C++11 标准的第 14.5.6.2 节对逻辑进行了布局。

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

【讨论】:

    【解决方案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&lt;int&gt;(3),两个模板都可以工作。事实上,如果你注释掉模板'a',对foo&lt;int&gt;(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 所说的那样更专业。

    【讨论】:

    • 很好的例子——说明这可能是多么违反直觉。
    猜你喜欢
    • 2013-01-17
    • 2011-07-19
    • 1970-01-01
    • 2019-06-04
    相关资源
    最近更新 更多