假设我有一个公共类和一个私有实现类(例如 PIMPL 模式),并且我希望用一个带有检查删除的模板智能指针类包装私有类,如下所示:
PublicClass.h
class PrivateClass;
// simple smart pointer with checked delete
template<class X> class demo_ptr
{
public:
demo_ptr (X* p) : the_p(p) { }
~demo_ptr () {
// from boost::checked_delete: don't allow compilation of incomplete type
typedef char type_must_be_complete[ sizeof(X)? 1: -1 ];
(void) sizeof(type_must_be_complete);
delete the_p;
}
private:
X* the_p;
};
// public-facing class that wishes to wrap some private implementation guts
class PublicClass
{
public:
PublicClass();
~PublicClass();
private:
demo_ptr<PrivateClass> pvt;
};
PublicClass.cpp
#include "PublicClass.h"
class PrivateClass
{
public:
// implementation stuff goes here...
PrivateClass() {}
};
//---------------------------------------------------------------------------
PublicClass::PublicClass() : pvt(new PrivateClass()) {}
PublicClass::~PublicClass() {}
main.cpp
#include "PublicClass.h"
int main()
{
PublicClass *test = new PublicClass();
delete test;
return 0;
}
此代码在 Visual C++ 2008 上编译成功,但在旧版本的 C++ Builder 上编译失败。特别是,main.cpp
由于demo_ptr<PrivateClass>::~demo_ptr
被 实例化而main.cpp
无法编译,并且该析构函数无法编译,因为它不能sizeof
在不完整的类型上执行PrivateClass
. 显然,编译器~demo_ptr
在 消费中实例化是没有用的main.cpp
,因为它永远无法生成合理的实现(看如何~PrivateClass
是不可访问的)。(PublicClass.cpp
在所有经过测试的编译器上都能正常编译。)
我的问题是:C++ 标准对模板类成员函数的隐式实例化有何看法?可能是以下之一?尤其是这些年来,这种情况发生了变化吗?
- 如果使用模板类,那么该类的所有成员函数都应该被隐式实例化——无论是否使用?
- 或者:如果实际使用,模板类函数应该一次只隐式实例化一个。如果不使用特定的模板类函数,则不应隐式实例化它——即使使用和实例化了其他模板类函数。
显然,今天的第二种情况就是这种情况,因为 PIMPL 及其检查删除使用了相同的模式unique_ptr
,但也许过去不是这种情况?过去第一种情况是可接受的编译器行为吗?
或者换句话说,编译器是否有问题,或者它是否准确地遵循了 C++98 标准,并且标准多年来发生了变化?
(有趣的事实:如果您在 C++ Builder 中删除选中的删除,并关闭函数内联,项目将愉快地编译。 PublicClass.obj
将包含正确的~demo_ptr
实现,main.obj
并将包含~demo_ptr
具有未定义行为的错误实现。使用的函数将取决于这些文件提供给链接器的顺序。)
更新:这是由于编译器错误,正如 Andy Prowl 所指出的,该错误在 C++ Builder XE8 中仍未修复。我已向 Embarcadero 报告了该错误: bcc32 编译器在使用带有 PIMPL 习惯用法的 std::auto_ptr 时导致未定义的行为,因为模板实例化规则不遵循 C++ 规范