【问题标题】:Instantiation of template function overloads模板函数重载的实例化
【发布时间】:2014-11-19 15:56:34
【问题描述】:

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

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

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

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

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

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

Live Sample

【问题讨论】:

  • 你的问题到底是什么?
  • @belwood 引用:Are overloads exempt from the lazy-instantiation rule for function templates?
  • @belwood:他或她想要解释函数模板隐式实例化规则。
  • 这不是严格意义上的语言律师问题,但它也适合标签,所以我添加了它。

标签: c++ language-lawyer


【解决方案1】:

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

更正式的答案是这两个模板都是候选模板,因为您的 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}
//              ^

(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]:实现不应隐式实例化不需要实例化的类模板的函数模板、变量模板、成员模板、非虚拟成员函数、成员类或静态数据成员. 如果虚拟成员函数不会被实例化,则未指定实现是否隐式实例化类模板的虚拟成员函数。 在默认参数中使用模板特化不会导致模板被隐式实例化,除非类模板可以在需要其完整类型来确定默认参数的正确性的情况下被实例化。在函数调用中使用默认参数会导致默认参数中的特化被隐式实例化。

【讨论】:

  • FWIW,如果它是“未指定的”,那么我将其视为不能保证,即不要依赖它。我的措辞比正确更务实,很抱歉造成误解。
  • 我用一个类的部分特化做了另一个测试,令人惊讶的是它仍然试图实例化这两个类。我不确定我做错了什么:ideone.com/YUFJp0
  • 实际上T 在我的部分专业化中也可以是一个指针,我需要一种专门研究非指针类型的方法(我认为),但不知道该怎么做。跨度>
  • @void.pointer:它没有试图实例化任何东西; zzz 只是格式错误,无论您编写它的专业化是否被实例化。
  • @void.pointer:MSVC 编译器历来对 C++ 的实现相当糟糕。不过,我有兴趣了解更多相关信息。
【解决方案2】:

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

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

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

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

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

因为nil 没有被实例化

【讨论】:

  • 是的,公平点。这里没有 SFINAE(zzz 不依赖于 T,甚至在语法上都无效),所以无论如何它都是不正确的。
  • 我在答案中添加了一个更短的演示。
  • 感谢您的提示 ;)
猜你喜欢
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
  • 2010-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多