【发布时间】:2013-03-31 05:50:48
【问题描述】:
假设我有一个公共类和一个私有实现类(例如 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 实例化,并且该析构函数无法编译,因为它不能对PrivateClass 的不完整类型执行sizeof。显然,编译器在消费main.cpp 中实例化~demo_ptr 是没有用的,因为它永远无法生成合理的实现(看看~PrivateClass 是如何不可访问的)。 (PublicClass.cpp 在所有经过测试的编译器上都能正常编译。)
我的问题是:C++ 标准对模板类的成员函数的隐式实例化有何规定?可能是以下之一?特别是,这些年来这种情况发生了变化吗?
- 如果使用模板类,那么该类的所有成员函数都应该被隐式实例化 - 无论是否使用?
- 或者:如果实际使用,模板类函数一次只能隐式实例化一个。如果未使用特定模板类函数,则不应隐式实例化它 - 即使使用和实例化了其他模板类函数。
似乎很明显,第二种情况是 today 的情况,因为 PIMPL 和 unique_ptr 使用了相同的模式及其检查删除,但也许过去不是这种情况?过去第一种情况是可接受的编译器行为吗?
或者换句话说,编译器是否存在错误,或者它是否准确地遵循了 C++98 标准,并且标准多年来发生了变化?
(有趣的事实:如果您在 C++ Builder 中删除选中的删除,并关闭函数内联,项目将顺利编译。PublicClass.obj 将包含正确的 ~demo_ptr 实现,main.obj 将包含不正确的~demo_ptr 具有未定义行为的实现。使用的函数将取决于将这些文件提供给链接器的顺序。)
更新:这是由于编译器错误,正如 Andy Prowl 所指出的,该错误在 C++ Builder XE8 中仍未修复。我已经向 Embarcadero 报告了这个错误: bcc32 compiler causes undefined behavior when using std::auto_ptr with PIMPL idiom because template instantiation rules do not follow C++ spec
【问题讨论】:
-
BTW
demo_ptr违反了第 3 条规则——你需要在它被双重删除之前解决这个问题。 -
“旧编译器”以及您所说的症状:可能是编译器错误。据我所知,没有一个 C++ 编译器完全符合语言规范。最新版本的 clang 可能是您最好的选择,但请理解,如果您使用的是非常老的编译器,您可能会遇到非常老的和错误的行为。
-
@BenVoigt:理解;为了简洁起见,这只是一个迂腐的测试用例。实际上我只是使用 auto_ptr。
-
你为什么用
auto_ptr? -
@Yakk:代码已经广泛使用它,运行时库还不支持unique_ptr。我们总有一天会换的。
标签: c++ windows c++11 c++builder