C++ 成员函数模板的隐式实例化
C++ Implicit instantiation of member function templates
我想更好地了解编译器何时隐式实例化成员函数模板。
请考虑以下示例:
// example.h
struct Parent {
virtual void foo(double) {}
};
struct Child : Parent {
void foo(double d) { bar(d); }
template<typename T> void bar(T);
virtual void baz();
};
// example.cpp
#include "example.h"
template <typename T>
void Child::bar(T) {}
void Child::baz() {}
用[g++|clang++] -c example.cpp
编译它,GCC和clang都隐式实例化函数模板Child::bar<double>
。但是,以下看似微小的更改阻止了这一点:
- 使
foo
不是虚拟
的 - 使
Child
不继承Parent
- 删除
baz
- 使
baz
不是虚拟
的 - 使用标头中的声明定义
baz
对于隐式实例化何时发生,是否有任何合理简洁的解释,或者我是否需要浏览标准的副本?我已经在SO中搜索了与隐式实例化有关的其他问题,但没有找到太多。我发现最接近的是这个问题,但它是关于类模板(不是成员函数模板)中的虚函数。
需要明确的是:我知道可以通过在标题中包含定义或显式实例化我需要的模板来避免这个问题。这只是在挖掘为什么一个类(我无意中省略了它的显式实例)仍然愉快地编译和链接时的一个好奇心。
我已经把你的代码放到Visual Studio 2013(child.h和child.cpp)中,并尝试了以下方法:
#include "child.h"
Child c1;
c1.bar(10.2);
这会产生一个未解决的外部错误,指示没有发生"隐式实例化"。 表明在这种情况下,Visual Studio和G ++之间存在明显差异。这是通过将 Child::foo 的代码移动到 child.cpp 文件中来修复的。
因此,简短的回答是:您正在经历的在很大程度上是特定于编译器的,对于可移植性,您不应该依赖此行为。根据C++标准,最安全的做法是将模板定义放在 .h(或 .hpp)文件中,或者根据需要显式实例化模板。任何其他管理此问题的方法都可能会在某些编译器中中断。
要进一步了解Visual Studio的行为,请看一下:
- https://msdn.microsoft.com/en-us/library/aa299373%28v=vs.60%29.aspx
- https://isocpp.org/wiki/faq/inline-functions#inline-member-fns
意思是,在类的定义中定义的任何函数都是隐式内联的。如果编译器愿意,它可以延迟内联函数的实例化。这意味着当编译 child.obj 时,永远不会创建 child::foo(d),这反过来意味着 bar 永远不会被实例化,因此这会导致链接阶段的编译问题。考虑到foo(double)在技术上是一个虚拟函数,为什么Visual Studio实际上可以做到这一点确实很奇怪,但是Visual Studio似乎将foo()的实例化留到以后使用Child时。所以例如:
Parent *p1 = new Child();
还会导致问题,因为此时编译器尝试创建 Child::foo(double) 并且由于缺少模板定义而无法创建。
从您的结果来看,假设 GCC 将立即实例化内联函数(如果它们是虚拟的)。
您正在经历的行为是多种因素的组合:
- 编译器如何处理隐式内联函数
- 如何为虚函数创建虚拟表
- 以及当编译器需要实例化虚拟成员函数时。
有关详细信息,请参阅以下问题:
- 什么时候在C++中创建 vtable?
- "内联"是否隐含在类定义中定义的成员函数C++
所以:
- 似乎允许编译器延迟内联函数的实例化,直到使用,然后决定是要"内联"它们还是创建一个单独的函数。
- 在使用类之前,编译器不需要完成对象所需的所有内容的实例化
- 事实上,即使在创建对象时,编译器也可能没有准备好在代码中为类定义的所有内容。
我决定不详细回答您的具体问题,因为我的研究似乎表明您的代码工作是间接的,并且不应该依赖这种行为。
- 静态数据成员模板专用化的实例化点在哪里
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 受约束的成员函数和显式模板实例化
- 实例化多种类型的成员函数模板
- 为什么在使用指针时不采用类成员的默认值,而不是直接实例化对象时?
- 将类成员函数的模板定义放在 CPP 文件中C++隐式实例化而不是 .H 允许吗?
- 如何实例化类的公共成员并将其作为 std::p romise 返回?
- 在实例化封闭类模板之后,我们可以声明模板类成员的部分专用化吗
- 使用 SFINAE 有选择地实例化模板的成员函数
- 静态模板成员函数的实例化?
- 访问使用接口实例化的类的私有成员
- Google Mock:在目标类的构造函数中实例化的模拟私有变量成员
- 参考数据成员到模板的实例化
- 类的私有成员在我的类实例化期间更改,即使他们不应该
- 如果未实例化成员模板,是否要评估static_asserts?
- 实例化与unique_ptr的类集合成员
- 实例化成员模板函数时的Buggy(?)编译器行为
- 类模板的成员函数模板找不到定义,尽管存在显式实例化。不链接
- 如何实例化C++成员字符串引用
- 通过模板参数选择子类与实例化成员变量的区别