模板函数重载的实例化

时间:2014-11-19 15:56:35

标签: c++ language-lawyer

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

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

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

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

int main()
{
    int* blah;
    foo(blah);
}

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

Live Sample

2 个答案:

答案 0 :(得分:6)

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

更正式的答案是两个模板都是候选模板,因为您的T总是可以被指针化,所以两者都在这个意义上被“使用”:

  

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

     

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

所以,基本上:

  

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

正确。

然而,你的问题源于一种误解,即你的第一个功能模板可以被忽略:它不可能。 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}
//              ^

live demo

话虽如此,在这种情况下诊断不是 ;特别是Microsoft Visual Studio has historically silently accepted such code

  

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

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

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

  

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

答案 1 :(得分:2)

这不是内部问​​题的答案,但它与问题的假设有关:&#34;如果没有实例化第一个重载,我会期望没有编译器错误,但是我得到了错误。&# 34;

不确定?那么,为什么这段代码会产生编译错误呢?

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

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

int main()
{
    int* blah;
    foo(blah);
}

因为nil未实例化