При использовании интеллектуальных указателей с идиомой pImpl, как в
struct Foo
{
private:
struct Impl;
boost::scoped_ptr<Impl> pImpl;
};
, очевидная проблема заключается в том, что Foo::Impl
является неполным в точке, где генерируется деструктор Foo
.
Компиляторы обычно выдают там предупреждение, и boost::checked_delete
, который используется внутренне интеллектуальными указателями Boost, статически утверждает, что класс Foo::Impl
завершен, и выдает ошибку, если это не так.
Чтобы скомпилировать приведенный выше пример, необходимо написать
struct Foo
{
~Foo();
private:
struct Impl;
boost::scoped_ptr<Impl> pImpl;
};
и реализовать пустой Foo::~Foo
в файле реализации, где Foo::Impl
завершено.Это преимущество умных указателей над голыми указателями, потому что мы не можем не реализовать деструктор.
Пока все хорошо.Но я столкнулся со странным поведением, когда я пытался представить конструктор шаблона в аналогичном классе Bar
(полный код, пожалуйста, попробуйте сами):
// File Bar.h
#ifndef BAR_H
#define BAR_H 1
#include <vector>
#include <boost/scoped_ptr.hpp>
struct Bar
{
template <typename I>
Bar(I begin, I end);
~Bar();
private:
struct Impl;
boost::scoped_ptr<Impl> pImpl;
void buildImpl(std::vector<double>&);
};
template <typename I>
Bar::Bar(I begin, I end)
{
std::vector<double> tmp(begin, end);
this->buildImpl(tmp);
}
#endif // BAR_H
// File Bar.cpp
#include "Bar.h"
struct Bar::Impl
{
std::vector<double> v;
};
void Bar::buildImpl(std::vector<double>& v)
{
pImpl.reset(new Impl);
pImpl->v.swap(v);
}
Bar::~Bar() {}
// File Foo.h
#ifndef FOO_H
#define FOO_H 1
#include <boost/scoped_ptr.hpp>
struct Foo
{
Foo();
~Foo();
private:
struct Impl;
boost::scoped_ptr<Impl> pImpl;
};
#endif // FOO_H
// File Foo.cpp
#include "Foo.h"
struct Foo::Impl
{};
Foo::Foo() : pImpl(new Impl)
{}
Foo::~Foo() {}
// File Main.cpp
#include "Foo.h"
#include "Bar.h"
int main()
{
std::vector<double> v(42);
Foo f;
Bar b(v.begin(), v.end());
}
При компиляции этого примера с Visual Studio 2005 SP1, Я получаю ошибку с Bar
, но не с Foo
:
1>Compiling...
1>main.cpp
1>c:\users\boost_1_45_0\boost\checked_delete.hpp(32) : error C2027: use of undefined type 'Bar::Impl'
1> c:\users\visual studio 2005\projects\checkeddeletetest\checkeddeletetest\bar.h(15) : see declaration of 'Bar::Impl'
1> c:\users\boost_1_45_0\boost\smart_ptr\scoped_ptr.hpp(80) : see reference to function template instantiation 'void boost::checked_delete<T>(T *)' being compiled
1> with
1> [
1> T=Bar::Impl
1> ]
1> c:\users\boost_1_45_0\boost\smart_ptr\scoped_ptr.hpp(76) : while compiling class template member function 'boost::scoped_ptr<T>::~scoped_ptr(void)'
1> with
1> [
1> T=Bar::Impl
1> ]
1> c:\users\visual studio 2005\projects\checkeddeletetest\checkeddeletetest\bar.h(16) : see reference to class template instantiation 'boost::scoped_ptr<T>' being compiled
1> with
1> [
1> T=Bar::Impl
1> ]
1>c:\users\boost_1_45_0\boost\checked_delete.hpp(32) : error C2118: negative subscript
1>c:\users\boost_1_45_0\boost\checked_delete.hpp(34) : warning C4150: deletion of pointer to incomplete type 'Bar::Impl'; no destructor called
1> c:\users\visual studio 2005\projects\checkeddeletetest\checkeddeletetest\bar.h(15) : see declaration of 'Bar::Impl'
Я попробую это с недавним GCC, как только я вернусь домой.
Я непонять, что происходит: в точке, где определен деструктор (т. е. в Bar.cpp
), доступно определение Bar::Impl
, поэтому проблем быть не должно.Почему это работает с Foo
, а не с Bar
?
Что мне здесь не хватает?