简体   繁体   中英

Why does boost::scoped_ptr prevent PIMPL idiom with BCB6?

I'm trying to use a boost::scoped_ptr with my implementation class that is visible only in the cpp file of the containing class. The containing class has an explicitly defined destructor (that isn't inline), but my compiler (Borland C++ 5.6.4) fails to compile.

If I use boost::shared_ptr instead, the same example compiles and runs as expected.

What am I doing wrong?


Edit: sorry for forgetting showing the source code, compiler error, and (expected) output here it is:

Source code

File check_shared.cpp :

// shortened.
#include "SmartPtrTest.h"
void check_shared()
{
    Containing t;
}

File SmartPtrTest.h :

#include <boost/noncopyable.hpp>
#include <boost/smart_ptr.hpp>

class Impl;
#define smart_ptr boost::scoped_ptr

class Containing: private boost::noncopyable
{
public:
    Containing();
    ~Containing();
private:
    smart_ptr<Impl> impl;
};

File SmartPtrTest.cpp :

#include "SmartPtrTest.h"
#include <iostream>

using namespace std;

class Impl {
public:
    Impl() {
        cout << "ctr Impl" << endl;
    }
    ~Impl() {
        cout << "dtr Impl" << endl;
    }
};

Containing::Containing(): impl(new Impl)
{
    cout << "ctr Containing" << endl;
}

Containing::~Containing()
{
    cout << "dtr Containing" << endl;
}

The compiler error

...is something like undefined structure 'Impl' (it's German: Undefinierte Struktur 'Impl' ). When compiling the file check_shared.cpp the compiler stops in the file boost/checked_delete.hpp in the typedef of this function:

template<class T> inline void checked_delete(T * x)
{
    // intentionally complex - simplification causes regressions
    typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
    (void) sizeof(type_must_be_complete);
    delete x;
}

The output (expected)

This output I'm getting when using boost::share_ptr , showing that ctr and dtr are called as expected.

ctr Impl
ctr Containing
dtr Containing
dtr Impl

This should work as long as the "implementation" class is complete at any point at which the smart pointer might be destroyed. This happens not just in the destructor, but in the constructors - these have to destroy the pointer member if they exit due to an exception.

So make sure that your constructors and destructor are defined in the source file, after the implementation class's definition.

(This is based on a guess you get a compilation error due to trying to destroy an incomplete type. If you get a different error, or unexpected runtime behaviour, or the change doesn't fix it, then please update the question to demonstrate the actual problem.)

This is most definitely due to a compiler bug. Unfortunately it still exists in C++ Builder XE8. See this related problem here, and the answer from Andy Prowl: Is it valid for a C++ compiler to implicitly instantiate ALL member functions of a template class?

Problem reported to Embarcadero here: bcc32 compiler causes undefined behavior when using std::auto_ptr with PIMPL idiom because template instantiation rules do not follow C++ spec

If you can use one of the newer clang-based compilers from Embarcadero, you should be ok. My test case passes in the 64-bit clang compiler.

UPDATE: We've worked around the issue by writing our own smart pointer class that's similar to unique_ptr in that it contains a checked_delete function that is not implemented inline inside the class. Instead, you have to explicitly instantiate the checked_delete function inside ONE translation unit that also contains the private class implementation. A macro is used to help with that. That is:

template<class X> class unique_ptr_bcc32 {
    // Deletes the object using operator delete:
    private: static void checked_delete(X* p);
    // <snip>
}

Later, in a CPP file:

class PIMPL { /* snip implementation */ };

// You can simplify this by way of a macro:
template <> void unique_ptr_bcc32<PIMPL>::checked_delete(PIMPL* p) {
    typedef char type_must_be_complete[sizeof(PIMPL) ? 1 : -1];
    static_cast<void>(sizeof(type_must_be_complete));
    delete p;
}

This leaves no room for funny business by the compiler when it comes to template instantiation and deleting.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM