模板函数重载的实例化

voi*_*ter 1 c++ language-lawyer

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

在一个简单的例子中,如果我有两个重载的模板函数,它们都采用相同的模板参数,似乎编译器实例化两个重载.我想这是必需的,以便编译器可以执行重载解析?重载是否免于功能模板的延迟实例化规则?我无法在标准中找到相关文本.这是一个例子:

template<typename T>
void foo(T) {zzz}

template<typename T>
void foo(T*) {}

int main()
{
    int* blah;
    foo(blah);
}
Run Code Online (Sandbox Code Playgroud)

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

现场样本

Lig*_*ica 6

看起来好像你只希望实例化其中一个重载,因为只会调用其中一个,但是编译器显然必须实例化它们,以确定是否可以调用它们中的任何一个,如果是的话,哪一个使用.

更正式的答案是两个模板都是候选模板,因为你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}
//              ^
Run Code Online (Sandbox Code Playgroud)

(现场演示)

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

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

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

顺便提一下,您对虚拟功能的观察也不完全准确:

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