C++ 模板函数重载解析

我有以下代码:

#include 模板 void f (T) { std::cout <<f(T)"<<std::endl;}模板 void f (bool) { std::cout <<"f(bool)" <<std::endl;}内部主(){f(真);//#1 打印 f(T)f<bool>(真);//#2 打印 f(bool)}

#1 行调用 f(T),而 #2 行调用 f(bool).

为什么会这样?以及选择重载模板函数的规则是什么?

更新

我知道在第一次调用中,编译器在尝试调用第二个函数时无法推断出T,所以选择了第一个.

在第二次调用中,第二个函数被认为在 gcc 上更匹配,而第一个是在 VS2013 下选择的.谁在这里做正确的事?顺便说一句,我仍然对过程的完整描述感兴趣.

解决方案

未特化的函数模板也称为底层基模板. 基模板可以是特化的.查看在不同情况下调用哪些方法的重载规则非常简单,至少在高层次上是这样:

  • 非模板函数是一等公民.与参数类型以及任何函数模板匹配的普通旧非模板函数将被选择,而不是其他一样好的函数模板.

  • 如果没有至少与二等公民一样好的一等公民可供选择,那么接下来将咨询二等公民的函数基础模板.根据一组相当神秘规则:

    • 如果很明显有一个最专业的"函数基础模板,那么就会使用那个模板.如果该基模板恰好专用于正在使用的类型,则将使用该特化,否则将使用使用正确类型实例化的基模板.

    • Else(如您的情况)如果最专业"的函数基模板存在联系,则调用是不明确的,因为编译器无法决定哪个更匹配.程序员必须做一些事情来限定调用并说明需要哪个.

    • 否则,如果没有可以匹配的函数基础模板,调用就会出错,程序员将不得不修复代码.

如果您想自定义函数基础模板并希望该自定义参与重载解析(或者,始终在完全匹配的情况下使用),请将其设为普通的旧函数,不是专业.而且,如果您确实提供了重载,请避免同时提供专业化.

以上摘自the Herb Sutter,在突出显示的项目符号中,您可以看到问题的根源

编辑

如果你在 Visual Studio 2012 中尝试(不要这样做)上面的代码,你会得到 <块引用>

致命错误 LNK1179:无效或损坏的文件:重复的 COMDAT '??$f@_N@@YAX_N@Z'

正如此处所解释的那样,是因为

<块引用>

你做了一些 C++ 无效的诡计",它通过了编译器,但你现在有一个无效的 *.obj,它阻塞了链接器.

下面这行是罪魁祸首

f(true);//#1 打印 f(T)

所以答案中解释的歧义没有保证解决

I have the following code:

#include <iostream>

template <typename T>
void f (T) { std::cout << "f(T)" << std::endl; }

template <typename T>
void f (bool) { std::cout << "f(bool)" << std::endl; }

int main ( )
{
    f(true);        // #1 prints f(T)
    f<bool>(true);  // #2 prints f(bool)
}

The #1 line calls f(T), while #2 line calls f(bool).

Why does this happen? And what are the rules for selecting an overloaded template function?

UPDATE

I understood that in the first call compiler is just unable to deduce T while trying to call the second function, so the first is chosen.

In the second call second function is considered a better match on gcc, while the first is chosen under VS2013. Who does the right thing here? By the way, I am still interested in the full description of the process.

解决方案

The unspecialized function templates are also called the underlying base templates. Base templates can be specialized. The overloading rules to see which ones get called in different situations, are pretty simple, at least at a high level:

  • Nontemplate functions are first-class citizens. A plain old nontemplate function that matches the parameter types as well as any function template will be selected over an otherwise-just-as-good function template.

  • If there are no first-class citizens to choose from that are at least as good, then function base templates as the second-class citizens get consulted next. Which function base template gets selected depends on which matches best and is the "most specialized" (important note: this use of "specialized" oddly enough has nothing to do with template specializations; it's just an unfortunate colloquialism) according to a set of fairly arcane rules:

    • If it's clear that there's one "most specialized" function base template, that one gets used. If that base template happens to be specialized for the types being used, the specialization will get used, otherwise the base template instantiated with the correct types will be used.

    • Else (as in your case) if there's a tie for the "most specialized" function base template, the call is ambiguous because the compiler can't decide which is a better match. The programmer will have to do something to qualify the call and say which one is wanted.

    • Else if there's no function base template that can be made to match, the call is bad and the programmer will have to fix the code.

If you want to customize a function base template and want that customization to participate in overload resolution (or, to always be used in the case of exact match), make it a plain old function, not a specialization. And, if you do provide overloads, avoid also providing specializations.

The above is an extract from this post by the Herb Sutter and in the highlighted bullet you can see the source of your problem

EDIT

If you try (don't do it) the above code with Visual Studio 2012, you get

fatal error LNK1179: invalid or corrupt file: duplicate COMDAT '??$f@_N@@YAX_N@Z'

which, as explained here, is because

You did some "trickery" that is invalid C++, and it passed the compiler, but you now have an invalid *.obj, and it chokes the linker.

and the following line is to blame

f(true);        // #1 prints f(T)

so the ambiguity explained in the answer has no guarandeed resolution

相关文章