即使我在 .cpp 文件中实例化一个虚拟对象,在 .cpp 文件中定义模板函数也不起作用

Posted

技术标签:

【中文标题】即使我在 .cpp 文件中实例化一个虚拟对象,在 .cpp 文件中定义模板函数也不起作用【英文标题】:Definition of a template function inside .cpp file is not working even though I am instantiating a dummy object in .cpp file 【发布时间】:2019-11-06 16:19:35 【问题描述】:

我理解了模板的概念以及为什么我们需要在头文件中定义模板成员函数。另一种选择是在 cpp 文件中定义模板函数并显式实例化模板类,如下所示。

template.h

#include <iostream>

using namespace std;

template <typename T> class myclass

    public:
        void doSomeThing();
;

template.cpp

#include <iostream>
#include "template.h"

using namespace std;

template <typename T> void myclass<T>::doSomeThing()

    cout << "in DoSomething" << endl;


template class myclass <int>; // Why we shouldn't use template<> class myclass <int> here?

ma​​in.cpp

#include <iostream>
#include "template.h"

using namespace std;

int main()

    myclass<int> obj;
    obj.doSomeThing();

我正在使用 g++ main.cpp template.cpp 在 Ubuntu 操作系统上编译,我可以调用 doSomeThing()

我有几个问题如下。

    如果我们需要显式实例化类模板,应该是 template &lt;&gt; class myclass &lt;int&gt; 但是当我在 template.cpp 而不是template class myclass &lt;int&gt;,它是 抛出对'myclass::doSomeThing()'的未定义引用 错误。为什么我们不应该在这种情况下使用&lt;&gt;

    我尝试将 myclass(用于 int)的对象实例化为 myclass <int> obj; 而不是 template &lt;&gt; class myclass &lt;int&gt;; in template.cpp 如下。

    #include <iostream>
    #include "template.h"
    
    using namespace std;
    
    template <typename T> void myclass<T>::doSomeThing()
    
        cout << "in DoSomething" << endl;
    
    
    myclass <int> obj;
    

    我认为 template.cpp 有通过 template.h 的模板声明和 template.cpp 中的所有模板函数定义,所以为 int 类型创建一个对象将为 int 创建一个类,它将包含 int 类型的函数定义。因此,当 g++ 编译 main.cpp 时,它具有 int 类型的所有功能,并且如果我先编译 template.cpp 并在 g++ template.cpp main.cpp 之后编译 main.cpp,则为 myclass(用于 int 数据类型)创建对象将起作用。但这也会引发 ma​​in.cpp:(.text+0x1f): undefined reference to `myclass::doSomeThing()' 错误。我无法理解为什么这会引发错误。谁能帮我理解为什么这不起作用。

【问题讨论】:

如果我们需要显式实例化类模板,应该是template &lt;&gt; class myclass &lt;int&gt; 为什么?谁告诉你的? 【参考方案1】:
    如果我们需要显式实例化类模板,应该是template &lt;&gt; class myclass &lt;int&gt;

不,不应该。这不是显式实例化的语法。 (这是模板专业化的语法。)

为什么我们不应该在这种情况下使用&lt;&gt;

因为这不是显式实例化的语法。正确的语法是:

template class|struct template-name < argument-list > ;

    我无法理解为什么这会引发错误。

因为模板没有明确的实例化定义。如果没有显式实例化,则模板可能无法在尚未定义模板的 TU 中使用。

【讨论】:

对于2,由于OP在源文件中有myclass &lt;int&gt; obj;,编译器不会隐式实例化int的模板吗? @NathanOliver-ReinstateMonica 是的。但它没有显式实例化它。 我相信还值得一提的是,当类模板实例化时,成员函数(如果没有删除)只是声明,而不是定义。【参考方案2】:

你的第一个问题是为什么要写作:

template<> class myclass <int>;

导致未定义的引用错误。答案是template&lt;&gt; 声明了一个明确的特化:它是说myclass&lt;int&gt;::doSomething 的一个特殊定义代替了文件前面的通用定义。但是,没有提供专业化的定义。因此,在翻译template.cpp 时,不会发出myclass&lt;int&gt;::doSomething 的定义。

您的第二个问题是为什么用以下方式替换显式实例化:

myclass <int> obj;

template.cpp 中没有导致从该翻译单元发出myClass&lt;int&gt;::doSomething。答案是双重的:

    当编译器在翻译 template.cpp 时隐式实例化 myclass&lt;int&gt; 时,它确实隐式实例化 myclass&lt;int&gt;::doSomething,因为此时还不需要函数的定义。

    即使编译器在template.cpp 的翻译期间隐式实例化myclass&lt;int&gt;::doSomething,也不能保证该隐式实例化使该实例化定义可用于其他翻译单元。 (编译器实际上可以通过内部链接生成它。)

参见 C++17 [temp]/7

函数模板、类模板的成员函数、变量模板或类模板的静态数据成员 类模板应在隐式实例化的每个翻译单元中定义(17.7.1),除非 相应的特化在某个翻译单元中被显式实例化(17.7.2);没有诊断是 必填。

这就是说,如果您需要从翻译单元 1 调用函数模板,但您只想在翻译单元 2 中定义该函数模板,那么翻译单元 2 必须显式实例化具有所需参数的函数模板。隐式实例化不算在内。

(注意myclass&lt;int&gt; 的显式实例化也会隐式实例化myclass&lt;int&gt; 的所有成员函数的定义。)

【讨论】:

以上是关于即使我在 .cpp 文件中实例化一个虚拟对象,在 .cpp 文件中定义模板函数也不起作用的主要内容,如果未能解决你的问题,请参考以下文章

编译器:“实例化对象”没有命名类型

JAVA虚拟机系列三-类加载过程双亲委派模型对象实例化过程

有没有办法在头文件中声明指针并在 .cpp 中实例化它?

实例化 ActiveX 对象

cppyy 模板类实例化没有虚拟析构函数

在 Unity 中销毁实例化游戏对象的问题