【问题标题】:How to do explicit template instantiation correctly?如何正确进行显式模板实例化?
【发布时间】:2012-02-01 10:18:22
【问题描述】:

我正在使用模板来实现 CRTP 模式。使用下面的代码,我得到了链接器错误(对于在基类CPConnectionBase 中定义的所有方法),如下所示:

错误 LNK2001:无法解析的外部符号“public: void __thiscall CPConnectionBase::start(void)”(?start@?$CPConnectionBase@VTNCPConnection@@@@QAEXXZ)

我想这里的解决方案是显式模板实例化。事实上,当我添加时我可以构建我的代码

#include "TNCPConnection.h"
template class CPConnectionBase<TNCPConnection>;

到文件 CPConnectionBase.cpp。这肯定是错误的地方,因为我不想将所有可能的派生类的标头包含到基类的源代码中(我可能还想在具有其他派生类的另一个项目中使用基类)。

所以我的目标是在派生类的源文件(TNCPConnection.h 或 TNCPConnection.cpp)中实例化模板,但我找不到解决方案。添加

template class CPConnectionBase<TNCPConnection>;

到文件 TNCPConnection.cpp 并没有解决我的链接器问题,并添加

template<> class CPConnectionBase<TNCPConnection>;

到文件 TNCPConnection.cpp 给我一个编译时错误:

错误 C2908:显式特化; 'CPConnectionBase' 已经被实例化了

如何在不使基类的实现依赖于派生类的头文件的情况下摆脱链接器错误?

这是我的代码框架:

CPConnectionBase.h

template <class Derived>
class CPConnectionBase : public boost::enable_shared_from_this<Derived>
{
public:
    void start();
};

CPConnectionBase.cpp

#include "stdafx.h"
#include "CPConnectionBase.h"

template<class Derived>
void CPConnectionBase<Derived>::start()
{
    ...
}

TNCPConnection.h

#include "CPConnectionBase.h"

class TNCPConnection : public CPConnectionBase<TNCPConnection>
{
public:
    void foo(void);
};

TNCPConnection.cpp

#include "stdafx.h"
#include "TNCPConnection.h"

void TNCPConnection::foo(void)
{
    ...
}

【问题讨论】:

    标签: c++ templates visual-c++ linker


    【解决方案1】:

    CPConnectionBase::start() 的定义必须在您显式实例化类的位置可用 - 否则该函数将不会被实例化,并且这种非实例化会静默发生(随后出现链接器错误)。

    标准解决方案是一个标头CPConnectionBase.hpp,它定义了CPConnectionBase.h 中声明的模板函数。在TNCPConnection.cpp 中包含CPConnectionBase.hpp 并在那里显式实例化。

    【讨论】:

      【解决方案2】:

      我应该在这里添加一个注释: MSVC 允许在同一编译单元内的成员函数之前声明显式特化。 GCC (4.7) 要求它们位于文件末尾。 即

      MSVC:

      template class TClass&lt;Base&gt;;

      template &lt;class T&gt; void TClass&lt;T&gt;::Function() {}

      海合会:

      template &lt;class T&gt; void TClass&lt;T&gt;::Function() {}

      template class TClass&lt;Base&gt;;

      【讨论】:

        【解决方案3】:

        首先,我建议将CPConnectionBase.cpp 重命名为CPConnectionBase.inl,因为它不包含任何非模板代码。

        在你实例化模板的时候,你需要先#include &lt;CPConnectionBase.inl&gt;。我建议在TNCPConnection.cpp 中进行。

        或者,您可以将CPConnectionBase 实现移动到CPConnectionBase.h 头文件,编译器将自动处理实例化。

        【讨论】:

          【解决方案4】:

          将模板化代码从 .cpp 移动到标头。

          当您在模板中包含标头时,模板化代码会根据在标头中找到的内容在目标代码中生成。

          如果代码在 .cpp 文件中,则找不到,因此无法生成(因为您只包含了 .h)

          【讨论】:

          • 我正在寻找一种可以分离声明和定义的解决方案。
          • 模板没有这样的解决方案。模板化代码需要在编译时可供编译器使用。因此,如果您还需要包含实现
          【解决方案5】:

          您也可以使用“分离模型”。只需在一个文件中定义模板,然后标记 使用关键字export定义:

          export template <class Derived>
          class CPConnectionBase : public boost::enable_shared_from_this<Derived>
          {
            public:
             void start();
          };
          

          【讨论】:

          • 我之前厌倦了这个,但在 VS2010 中它说“警告 C4237:'export' 关键字尚不支持,但保留供将来使用”
          • Um ... export 已从当前的 C++ 标准中删除,因此不鼓励使用 IMO。
          • VS 从来不支持导出
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-11-25
          • 2011-06-23
          • 1970-01-01
          • 2018-02-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多