这个模板参数推导是如何工作的?

3

编译器在不知道 foo 函数模板参数 T 的类型时,是如何决定调用 bar 函数的呢?

通常情况下,当我们调用 foo(2) 时,通过参数 2 推断出 T 的类型是 int

而在这里,T 是根据函数 bar 的参数来推导的,而 foo 函数则将其作为参数传递给了 bar 函数。

#include <iostream>

template<typename T>
void foo(const T& a_) {
    std::cout << a_<< std::endl;
}

void bar(void (*ptr) (const int&)) {
    std::cout << "bar called" << std::endl;
}

int main() {
    bar(foo);
}

编译器: gcc


3
类型在酒吧参数中。 - apple apple
1
就像 void (*ptr) (const int&) = foo; 一样工作。 - apple apple
1
同时 auto* p = static_cast<void (*) (const int&)>(foo); - apple apple
@john:不是很清楚。你遇到了什么问题?bar接受一个函数指针ptr作为参数,而int版本的bar可以匹配它。 - nick
1
函数模板可以转换为具体的函数指针。 - NathanOliver
显示剩余5条评论
2个回答

2
当你有一个重载集,比如 foo,意味着对于 foo 的名称查找结果可能会得到多个非模板函数或函数模板,并且如果该重载集的地址或引用被获取(这里通过将其作为函数参数隐式传递),那么编译器将尝试针对目标类型进行重载解析。这里的目标类型是 void(*)(const int&)。整个过程在 [over.over] 中有详细说明,下面我将解释其中的部分。
这个过程的工作原理是,编译器将查看重载集中的每个函数和函数模板,检查它是否可以与目标类型匹配,并将匹配的函数和函数模板特化添加到一组已选重载中。然后,还会进行一些消除步骤,以减少所选重载的集合(例如,检查是否满足相关约束条件并优先选择函数而不是函数模板特化,请参见[over.over]/5以获取所有详细信息)。希望最终只剩下一个选定的函数或函数模板特化,它将被选为重载解析的结果,原始名称(foo)将引用该函数或函数模板特化,并从中获取地址/引用。
对于非模板函数,匹配意味着目标的函数类型(void(const int&))需要与函数的类型完全相同。但是这里没有任何非模板重载。
对于函数模板,编译器将尝试执行模板参数推导,以选择与类型匹配并可以添加到所选重载列表中的模板的一个特化。具体来说,这与在函数调用的模板参数推导中通常有一个函数参数/形参对列表来推导模板参数的方式完全相同,但在这种情况下(假设有一个目标类型),推导将只考虑一个参数/形参对,其中参数是目标类型(void(*)(const int&)),而形参是包含模板参数的模板的函数类型,即此处的 void(const T&),调整为指针类型 void(*)(const T&),因为参数是函数指针类型。
因此,最终编译器将执行正常的推导。
void(*)(const int&)

反对

void(*)(const T&)

决定所选专业化中T应该是什么。我想很明显,T应该是int,以使类型匹配。因此,所选的专业化将是具有与目标类型匹配的函数类型的T = int,由于它是唯一被选中的重载,并且在任何进一步的步骤中都不会被排除,因此它将被选择为重载分辨率的结果。


还发现了以下参考资料...
  1. 模板参数推导Address of an overload set标题下
  2. overloaded_address
- Vivek
@Vivek 是的,cppreference上的文章可能更容易理解。我给出的链接是指规定行为的实际标准文件的草案版本的呈现。 - user17732522

0

它实际上是从函数指针的类型中推断出T的参数,这基本上是如何工作的。

  • 首先编译器看到您调用了接受返回void和一个参数为const int&类型的函数指针的bar
  • 然后它看到传递的参数是函数模板。然后它开始想知道是否可以实例化该函数,使其返回类型和参数列表与函数指针类型相对应。
  • 哇。编译器确实可以通过删除cv限定符和引用来实现这一点。 T似乎是int

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接