使用extern template
使用std::unique_ptr<T>
whereT
是不完整类型的问题是unique_ptr
需要能够删除T
各种操作的实例。该类unique_ptr
用于std::default_delete<T>
删除实例。因此,在理想世界中,我们只需写
extern template class std::default_delete<T>;
以防止std::default_delete<T>
被实例化。然后,声明
template class std::default_delete<T>;
在完成的地方T
,将实例化模板。
这里的问题是default_delete
实际上定义了不会被实例化的内联方法。所以,这个想法行不通。但是,我们可以解决这个问题。
首先,让我们定义一个不内联调用运算符的删除器。
/* --- opaque_ptr.hpp ------------------------------------------------------- */
#ifndef OPAQUE_PTR_HPP_
#define OPAQUE_PTR_HPP_
#include <memory>
template <typename T>
class opaque_delete {
public:
void operator() (T* ptr);
};
// Do not move this method into opaque_delete, or it will be inlined!
template <typename T>
void opaque_delete<T>::operator() (T* ptr) {
std::default_delete<T>()(ptr);
}
此外,为了便于使用,定义一个opaque_ptr
与 结合unique_ptr
的类型opaque_delete
,并且类似于std::make_unique
,我们定义make_opaque
。
/* --- opaque_ptr.hpp cont. ------------------------------------------------- */
template <typename T>
using opaque_ptr = std::unique_ptr<T, opaque_delete<T>>;
template<typename T, typename... Args>
inline opaque_ptr<T> make_opaque(Args&&... args)
{
return opaque_ptr<T>(new T(std::forward<Args>(args)...));
}
#endif
该类型opaque_delete
现在可以与extern template
构造一起使用。这是一个例子。
/* --- foo.hpp -------------------------------------------------------------- */
#ifndef FOO_HPP_
#define FOO_HPP_
#include "opaque_ptr.hpp"
class Foo {
public:
Foo(int n);
void print();
private:
struct Impl;
opaque_ptr<Impl> m_ptr;
};
// Do not instantiate opaque_delete.
extern template class opaque_delete<Foo::Impl>;
#endif
由于我们阻止opaque_delete
实例化此代码,因此编译时不会出错。为了让链接器满意,我们opaque_delete
在我们的foo.cpp
.
/* --- foo.cpp -------------------------------------------------------------- */
#include "foo.hpp"
#include <iostream>
struct Foo::Impl {
int n;
};
// Force instantiation of opaque_delete.
template class opaque_delete<Foo::Impl>;
其余方法可以如下实现。
/* --- foo.cpp cont. -------------------------------------------------------- */
Foo::Foo(int n)
: m_ptr(new Impl)
{
m_ptr->n = n;
}
void Foo::print() {
std::cout << "n = " << m_ptr->n << std::endl;
}
这种解决方案的优点是,一旦opaque_delete
定义,所需的样板代码就相当小。