2

我正在尝试创建一个带有模板化超类的 C++ 类。这个想法是,我可以很容易地从许多具有相似特征的超类中创建许多相似的子类。

我将有问题的代码提炼如下:

template_test.h

template<class BaseClass>
class Templated : public BaseClass
    {
public:
    Templated(int a);
    virtual int Foo();
    };

class Base
    {
protected:
    Base(int a);
public:
    virtual int Foo() = 0;
protected:
    int b;
    };

template_test.cpp

#include "template_test.h"

Base::Base(int a)
    : b(a+1)
    {
    }

template<class BaseClass>
Templated<BaseClass>::Templated(int a)
    : BaseClass(a)
    {
    }

template<class BaseClass>
int Templated<BaseClass>::Foo()
    {
    return this->b;
    }

main.cpp

#include "template_test.h"

int main()
    {
    Templated<Base> test(1);
    return test.Foo();
    }

Templated<Base>::Templated(int)当我构建代码时,我得到链接器错误,说Templated<Base>::Foo()找不到符号。

一个快速的谷歌建议添加以下内容main.cpp将解决问题:

template<> Templated<Base>::Templated(int a);
template<> int Templated<Base>::Foo();

但这并不能解决问题。添加行main.cpp也不起作用。(虽然,有趣的是,将它们添加到两者都会导致链接器出现“多重定义的符号”错误,因此它们必须在做某事......)

但是,将所有代码放在一个源文件中确实可以解决问题。虽然这对于上面的点头示例来说是可以的,但如果我被迫将全部内容放在一个 cpp 文件中,我正在查看的实际应用程序将很快变得难以管理。

有谁知道我正在做的事情是否可能?(如何)我可以解决我的链接器错误吗?

我会假设我可以将所有方法都设置为class Templatedinline 并且这会起作用,但这似乎也不理想。

4

4 回答 4

4

对于模板类,定义必须可用于使用它的每个翻译单元。定义可以放在单独的文件中,通常带有.inl.tcc扩展名;头文件#include是底部的那个文件。因此,即使它在一个单独的文件中,它仍然#include是每个翻译单元的 d ;它不能是独立的。

因此,对于您的示例,重命名template_test.cpptemplate_test.inl(或template_test.tcc,或其他),然后将#include "template_test.inl"(或其他)放在 的底部template_test.h,就在#endif包含守卫的之前。

希望这可以帮助!

于 2008-10-16T08:07:22.083 回答
2

问题是当你的模板文件被编译时,编译器不知道它需要为什么类型生成代码,所以它不知道。

然后当你链接时,main.cpp 说它需要这些函数,但它们从未编译到目标文件中,所以链接器找不到它们。

其他答案显示了以可移植方式解决此问题的方法,实质上是将模板化成员函数的定义放在您实例化该类实例的位置可见 - 通过显式实例化或将实现放入从 main.cpp #included 的文件。

You may also want to read your compiler's documentation to see how they recommends setting things up. I know the IBM XLC compiler has some different settings and options for how to set these up.

于 2008-10-16T14:12:11.257 回答
1

C++ FAQ-lite 涵盖了这一点,并有几种方法可以绕过它。

您不必使所有方法都“内联”,但您应该在 template_test.h 中定义方法主体,而不是在 template_test.cpp 中。

一些编译器可以处理这种拆分,但您必须记住,在某一层面上,模板就像宏。为了让编译器为您的特定模板生成模板,它需要手边有模板源。

于 2008-10-16T08:11:24.683 回答
-1

当编译器编译 main.cpp 时,它看到类定义有成员函数声明,但没有成员函数定义。它只是假设在某处必须有“模板化”构造函数和 Foo 实现的定义,因此它遵循链接器在链接时找到它。

您的问题的解决方案是将 Templated 的实现放入 template.h 中。

例如

template<class BaseClass>
class Templated : public BaseClass
    {
public:
    Templated(int a) : BaseClass(a) {}
    virtual int Foo() { return BaseClass::b; }
    };

有趣的是,我可以通过将它放在 template_test.cpp 的末尾来链接您的代码。

void Nobody_Ever_Calls_This()
{
    Templated<Base> dummy(1);
}

现在编译器可以找到要链接的 Templated 实例。我不建议将此作为一种技术。其他一些文件可能想要创建一个

Templated<Widget>

然后您必须向 template_test.cpp 添加另一个显式实例化。

于 2008-10-16T08:31:02.210 回答