模板函数重载的实例化

Instantiation of template function overloads

本文关键字:实例化 重载 函数      更新时间:2023-10-16

我知道编译器不会实例化未使用的模板函数,只要它们在类中不是虚拟的。

在一个简单的例子中,如果我有两个重载的模板函数,它们都采用相同的模板参数,那么编译器似乎会实例化这两个重载。我想这是必需的,以便编译器可以执行重载解决?重载是否不受函数模板的延迟实例化规则的约束?我无法在标准中找到相关文本。下面是一个示例:

template<typename T>
void foo(T) {zzz}
template<typename T>
void foo(T*) {}
int main()
{
    int* blah;
    foo(blah);
}

如果没有实例化第一个重载,我希望没有编译器错误,但是我得到了错误。

现场样品

似乎您只期望实例化其中一个重载,因为只会调用其中一个重载,但编译器显然必须实例化它们中的任何一个,以确定是否可以调用它们中的任何一个,如果是,则使用哪一个。

更正式的答案是,这两个模板都是候选模板,因为您的T始终可以指向,因此从这个意义上说,两者都是"使用"的:

[C++14: 14.7.1/3]: 除非函数模板专用化已显式实例化或显式专用化,否则当在需要存在函数定义的上下文中引用专用化时,将隐式实例化函数模板专用化。除非调用函数模板显式专用化或显式专用类模板的成员函数,否则在需要默认参数值的上下文中调用函数时,将隐式实例化函数模板或类模板的成员函数的默认参数。

[C++14: 14.7.1/10]: 如果函数模板或成员函数模板专用化以涉及重载解析的方式使用,则会隐式实例化专用化声明 (14.8.3)。

所以,基本上:

我想这是必需的,以便编译器可以执行重载解决?

正确。

但是,您的问题已经源于一种误解,即您的第一个函数模板可以忽略:它不可能。 zzz不依赖于任何模板参数,因此不涉及 SFINAE;即使涉及 SFINAE,它也无法帮助您处理无效语法。因此,无论您做什么,该代码的格式都是错误的:

template<typename T>
void nil() {zzz}
// g++ -c -std=c++11 -O2 -Wall -pedantic -pthread main.cpp
// main.cpp: In function 'void nil()':
// main.cpp:2:13: error: 'zzz' was not declared in this scope
//  void nil() {zzz}
//              ^

(现场演示)

话虽如此,在这种情况下不需要诊断;特别是,Visual Studio历来Microsoft默地接受这样的代码:

[C++14: 14.6/8]: 知道哪些名称是类型名称可以检查每个模板的语法。不得对可以生成有效专业化的模板发布诊断。如果无法为模板生成有效的专用化,并且该模板未实例化,则模板格式不正确,无需诊断。如果可变参数模板的每个有效专用化都需要一个空的模板参数包,则该模板格式不正确,无需诊断。如果在非依赖名称中使用的类型在定义模板时不完整,但在完成实例化时已完成,并且如果该类型的完整性影响程序的格式是否正确或影响程序的语义,则程序格式不正确;无需诊断。[..]

在C++11和C++03中也可以找到相同的措辞,因此情况一直如此。因此,您的误解是可以理解的。

顺便说一下,你对虚函数的观察也不完全准确:

[C++14: 14.7.1/11]:实现不得隐式实例化不需要实例化的函数模板、变量模板、成员模板、非虚拟成员函数、成员类或类模板的静态数据成员。如果虚拟成员函数不会被实例化,则不指定实现是否隐式实例化类模板的虚拟成员函数。在默认参数中使用模板专用化不会导致模板被隐式实例化,除非类模板可能需要其完整类型来确定默认参数的正确性。在函数调用中使用默认参数会导致隐式实例化默认参数中的专用化。

这不是来自内部问题的答案,而是与问题的假设有关:"如果没有实例化第一个重载,我希望不会发生编译器错误,但是我得到了错误。

当然可以吗?那么,为什么这段代码会生成编译器错误呢?

template<typename T>
void nil(T) {zzz}
template<typename T>
void foo(T*) {}
int main()
{
    int* blah;
    foo(blah);
}

因为nil未实例化