使用するextern template
std::unique_ptr<T>
where T
isが不完全なタイプを使用する場合の問題は、さまざまな操作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
定義されると、必要な定型コードがかなり小さいことです。