在从抽象类继承的模板类中正确使用未定义的类型

Proper use of undefined type in template class inheriting from abstract class

本文关键字:类型 未定义 抽象类 继承      更新时间:2023-10-16

我有一个头文件(比如the_foo.h),它定义/声明了以下类:

// file the_foo.h
class FooBase
{
    virtual size_t bar_size() = 0;
};
template<class Bar>
class Foo : public FooBase
{
    size_t  bar_size()  { return sizeof(Bar); }
};
class TheBar;       // TheBar forward declaration
class TheFoo : public Foo<TheBar>
{
};

使用MS vc14(Visual Studio 2015)编译时,我注意到以下行为:

  1. 任何包含_foo.h并定义TheBar的cpp文件,例如:

    #include "the_foo.h" class TheBar {}; // TheBar definition

    会编译得很好。然而,

  2. 任何包含_foo.h的cpp文件都不会定义TheBar,例如:

    #include "the_foo.h"

    编译失败,出现错误:the_foo.h(11): error C2027: use of undefined type 'TheBar'

    上面编译的cpp文件只包含一行:包含标头,不再包含代码
    删除虚拟成员函数声明virtual size_t bar_size() = 0;确实修复了该错误
    感谢Sam Varshavchik的回答,这段代码使用gcc 5.3.1编译得很好,所以这个问题显然是编译器特有的。

我的问题是:

  • TheBar仅向前声明时,为什么在情况(2)中编译失败
  • 有没有任何方法可以使case(2)在vc14下成功编译,即没有TheBar的明确定义,我需要在一些cpp文件中保持哪个类的不透明性

附言:我编辑了这个问题的代码示例,以明确问题中描述的问题的实际原因。原始代码样本(Sam Varshavchik的回答中引用了该样本)可能确实对问题的实际原因产生了误导,因此导致了问题范围之外的答案和评论。

您的测试用例编译时没有出现gcc 5.3.1.问题

$ cat t.C
class FooBase
{
public:
    FooBase()          {}
    virtual ~FooBase() {}
};
template<class Bar>
class Foo : public FooBase
{
public:
    Foo()   { sizeof(Bar); bar = new Bar; }
    ~Foo()  { sizeof(Bar); delete bar; }
    Bar*    bar;
};
class TheBar;
class TheFoo : public Foo<TheBar>
{
public:
    TheFoo();
    ~TheFoo();
};
[mrsam@octopus tmp]$ g++ -c -o t.o t.C
[mrsam@octopus tmp]$ 

这里的答案似乎是"你使用的是一个旧的C++编译器,它没有正确编译这个"。

当您试图在Foo类的构造函数和析构函数中构造或删除class TheBar时,编译器会搜索它的定义。这意味着它需要在这一点上实现,否则它不知道该做什么

如果做以下例子:

第一个标题:

// foobase.h
class FooBase
{
    public:
        FooBase()          {}
        virtual ~FooBase() {}
};
template<class Bar>
class Foo : public FooBase
{
    public:
        Foo()  { bar = new Bar; }
        ~Foo() { delete bar; }
    private:
        Bar* bar;
};
class TheBar;
class TheFoo : public Foo<TheBar>
{
    public:
        TheFoo() {};
        ~TheFoo() {};
};

下一个标题

// thebar.h
class TheBar {};

以及以下主文件:

// foo_main.cxx
// #include "thebar.h" // Including this include makes the program run
#include "foobase.h"
int main()
{
    TheFoo thefoo;
    return 0;
}

然后你的编译器会告诉你什么是错误的(只显示第一个错误):

./foobase.h:14:32: error: allocation of incomplete type 'TheBar'
        Foo()  { bar = new Bar; }

包括thebar.h将解决这个问题。