ItemContainer
ソート、インデックス作成、グループ化などのさまざまな機能を備えたコンテナのファミリ全体のファサードであるテンプレートクラスがあります。
実装の詳細は、cpp.
pimplイディオムと明示的なインスタンス化を使用してファイルに隠されています。テンプレートは、コンテナの実際の動作を定義する、よく知られた限定された実装クラスのセットでのみインスタンス化されます。
メインテンプレートは、すべてのコンテナでサポートされている共通の機能を実装しています- IsEmpty()
、など。GetCount()
Clear()
それぞれの特定のコンテナは、それによってのみサポートされるいくつかの機能を専門としています。たとえばSort()
、ソートされたコンテナ、operator[Key&]
キーインデックス付きコンテナなどです。
そのようなデザインの理由は、クラスが90年代初頭にいくつかの先史時代によって書かれたいくつかのレガシーの手作り自転車コンテナの代わりになるためです。アイデアは、古い腐敗した実装を最新のSTL&Boostコンテナに置き換え、古いインターフェイスを可能な限りそのままにしておくことです。
問題
このような設計は、ユーザーがサポートされていない関数を特定の専門分野から呼び出そうとすると、不快な状況につながります。コンパイルはOKですが、リンクステージでエラーが発生します(シンボルが定義されていません)。あまりユーザーフレンドリーな動作ではありません。
例:
SortedItemContainer sc;
sc.IsEmpty(); // OK
sc.Sort(); // OK
IndexedItemContainer ic;
ic.IsEmpty(); // OK
ic.Sort(); // Compiles OK, but linking fails
もちろん、特殊化の代わりに継承を使用することで完全に回避することはできますが、1〜3個の関数を持つクラスをたくさん作成するのは好きではありません。オリジナルのデザインを維持したい。
リンクステージ1ではなくコンパイルステージエラーになる可能性はありますか?どういうわけか静的アサーションが使える気がします。
このコードのターゲットコンパイラはVS2008であるため、実用的なソリューションはC ++ 03互換である必要があり、MS固有の機能を使用できます。ただし、ポータブルC++11ソリューションも歓迎します。
ソースコード:
// ItemContainer.h
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <class Impl> class ItemContainer
{
public:
// Common functions supported by all specializations
void Clear();
bool IsEmpty() const;
...
// Functions supported by sequenced specializations only
ItemPtr operator[](size_t i_index) const;
...
// Functions supported by indexed specializations only
ItemPtr operator[](const PrimaryKey& i_key) const;
...
// Functions supported by sorted specializations only
void Sort();
...
private:
boost::scoped_ptr<Impl> m_data; ///< Internal container implementation
}; // class ItemContainer
// Forward declarations for pimpl classes, they are defined in ItemContainer.cpp
struct SequencedImpl;
struct IndexedImpl;
struct SortedImpl;
// Typedefs for specializations that are explicitly instantiated
typedef ItemContainer<SequencedImpl> SequencedItemContainer;
typedef ItemContainer<IndexedImpl> IndexedItemContainer;
typedef ItemContainer<SortedImpl> SortedItemContainer;
// ItemContainer.cpp
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation classes definition, skipped as non-relevant
struct SequencedImpl { ... };
struct IndexedImpl { ... };
struct SortedImpl { ... };
// Explicit instantiation of members of SequencedItemContainer
template void SequencedItemContainer::Clear(); // Common
template bool SequencedItemContainer::IsEmpty() const; // Common
template ItemPtr SequencedItemContainer::operator[](size_t i_index) const; // Specific
// Explicit instantiation of members of IndexedItemContainer
template void IndexedItemContainer::Clear(); // Common
template bool IndexedItemContainer::IsEmpty() const; // Common
template ItemPtr IndexedItemContainer::operator[](const PrimaryKey& i_key) const; // Specific
// Explicit instantiation of members of SortedItemContainer
template void SortedItemContainer::Clear(); // Common
template bool SortedItemContainer::IsEmpty() const; // Common
template void SortedItemContainer::Sort(); // Specific
// Common functions are implemented as main template members
template <class Impl> bool ItemContainer<Impl>::IsEmpty() const
{
return m_data->empty(); // Just sample
}
// Specialized functions are implemented as specialized members (partial specialization)
template <> void SortedItemContaner::Sort()
{
std::sort(m_data.begin(), m_data.end(), SortFunctor()); // Just sample
}
...
// etc