防止实例化具有不完整类型的模板类 [英] Prevent instantiation of template class with an incomplete type

查看:247
本文介绍了防止实例化具有不完整类型的模板类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在写图书馆.其布局类似于以下内容:

I'm writing a library. Its layout looks something akin to this:

/////////
// A.h //
/////////

#include <vector>

class B;

class A
{
    std::vector<B> Bs;

public:
    ...
};

/////////
// B.h //
/////////

class B
{
    ...
}

///////////
// A.cpp //
///////////

#include "A.h"
#include "B.h"

// Implementation of A follows
...

///////////
// B.cpp //
///////////

#include "B.h"

// Implementation of B follows
...

/////////////
// MyLib.h //
/////////////

#include "A.h"

如您所见,从外部可以访问的唯一类型应该是A,这就是为什么BA.h中被声明为不完整类型的原因.库本身可以很好地编译,但是在程序中使用它时,编译器会发出如下错误:invalid use of incomplete type B,当我尝试创建类型为A的对象时.这些错误指向vector标头,特别是指向std::vector的析构函数,显然,该析构函数需要知道其持有的类型的大小才能正确地重新分配内部存储.我认为正在发生的事情是编译器正在尝试在我的程序中实例化std::vector<B>::~vector,由于上述原因,该实例无法成功.但是,库的二进制文件中有std::vector<B>::~vector的符号,因此无需在程序中实例化它就可以做到.我正在寻找一种告诉编译器的方法.我已经尝试过将MyLib.h更改为以下内容:

As you can see, the only type accessible from the outside is supposed to be A, that's why B is declared as an incomplete type in A.h. The library itself compiles fine, but when it comes to using it in a program, the compiler issues errors like: invalid use of incomplete type B when I try to create an object of type A. These errors point to the vector header, specifically, to the std::vector's destructor, which apparently needs to know the size of the type it holds to deallocate the internal storage properly. What I think is happening is that the compiler is trying to instantiate std::vector<B>::~vector in my program, which can't succeed for aforementioned reasons. However, there are symbols for std::vector<B>::~vector in the library's binary, so I could do without instantiating it in the program. I'm looking for a way to tell that to the compiler. I've already tried changing MyLib.h to something like this:

/////////////
// MyLib.h //
/////////////

#include "A.h"
extern template class std::vector<B>;

不幸的是,这不起作用,因为extern仅适用于编译的代码生成阶段,并且编译器在解析时仍报告错误.我认为编译器可能正在尝试实例化std::vector<B>::~vector,因为A具有隐式析构函数,因此我尝试像这样手动实现析构函数:

This unfortunately doesn't work, because extern applies only to the code generation stage of the compilation, and the compiler still reports errors while parsing. I thought that maybe the compiler is trying to instantiate std::vector<B>::~vector because A has an implicit destructor, so I tried implementing a destructor manually like this:

/////////
// A.h //
/////////

#include <vector>

class B;

class A
{
    std::vector<B> Bs;
    ~A();

public:
    ...
};

///////////
// A.cpp //
///////////

#include "A.h"
#include "B.h"

A::~A() {}

// Further implementation of A follows
...

这也无济于事,尽管现在不应该在库代码之外调用编译器,但编译器仍在尝试实例化std::vector<B>::~vector.是否有其他方法可以实现我想要的目标,还是最好为B选择不同的信息隐藏方法?

This doesn't help either, the compiler is still trying to instantiate std::vector<B>::~vector, even though it shouldn't ever be invoked outside the library code now. Is there a different way to achieve what I want or would it be best to choose a different method of information hiding for B?

推荐答案

注意,可能会变黑:

ATTENTION, POSSIBLE DARK MAGIC: I am not sure whether (or since when) one is allowed - by the standard - to define a member std::vector<T> with an incomplete type T if none of the vectors member functions (including constructors and destructor) is referenced from the current translation unit. I think C++17 allows this, though.

自C ++ 11起可以编译的一种可能性(尽管可能是非法的,请参见上文)是使用union成员以避免对<的构造函数和析构函数的调用c6>成员:

One possibility that compiles since C++11 (though it could be illegal, see above) is the use of an union member to avoid calls to both the constructor(s) as well as to the destructor of the std::vector member:

struct Hidden;

struct Public {
    union Defer {
        std::vector<Hidden> v;
        Defer();
        // add copy/move constructor if needed
        ~Defer();
    } d;
};

现在,在实现文件中,您可以(并且必须)实际调用向量的构造函数和析构函数:

Now, in your implementation file, you can (and must) actually call the constructor(s) and destructor of the vector:

struct Hidden { /* whatever */ };
Public::Defer::Defer() { new (&v) std::vector<Hidden>(); }
Public::Defer::~Defer() { v.~vector<Hidden>(); }

当然,以任何方式使用成员d.v都需要Hidden 的定义.因此,您应该将此类使用限制为Public的(非inline)成员函数,这些成员函数是在可以访问Hidden完整定义的文件中实现的.

Of course, using the member d.v in any way will need the definition of Hidden. Thus you should limit such uses to the (non inline) member functions of Public, which you implement in file(s) that have access to the complete definition of Hidden.

这篇关于防止实例化具有不完整类型的模板类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆