関数テンプレートを部分的に特殊化することは不可能であるため、ユーザー定義のテンプレート化されたクラスのstd::hash
場合、それが関数であれば特殊化する方法はありません。std
(名前空間からのみテンプレートを特殊化できますが、オーバーロードはできません。そのため、テンプレート化されたクラスのユーザーは を作成できずstd::unordered_map<MyClass<whatever>, MyOtherClass>
、選択を余儀なくされますstd::unordered_map<MyClass<whatever>, MyOtherClass, ???>
)。したがって、ファンクターはここでのソリューションです。
namespace std
{
template<typename T>
struct hash<MyVector<T>>
{
size_t operator()(const MyVector<T>& v)
{
//return hash from here
}
};
}
標準ライブラリの別の方法は、SFINAE テンプレート トリックを使用してメンバー.hash()
をデフォルトとして選択し、それ以外の場合は標準ハッシュを使用することですが、ほとんどの場合、インターフェイスを改造することはできません (特にサードパーティ コードを使用する場合)。
他の代替手段は次のようになりますstd::swap
(ADLを使用したトリック):
//somewhere in std::unordered_map
using std::hash;
size_t h = hash(key);
私の経験では、ADL はトリッキーであり、誰もがコーナー ケースについて覚えているわけではありません。さらに、ここでのファンクターの利点は、それらをテンプレートパラメーターとして使用できるという事実です。したがってstd::unordered_map<A, B, specialized_hash<A>>
、デフォルトがケースに不適切であると思われる場合は、テンプレート用の別のファンクター ( など) をプラグインするだけで済みます。
コメントから:
しかし、std::swap についてもう少し詳しく説明していただけますか? C++11 にはまだありますし、ユーザー定義型に問題はありませんね。STL の一貫性を高めるのではなく、多くの異なる概念を STL に保持するのはなぜですか?
と の間には少し違いがstd::swap
ありstd::hash
ます:
でstd::hash
:
- たとえば
std::string
、クラス作成者によって定義されたハッシュはあなたの場合には十分ではないでしょう。つまり、あまりにも一般的であり、ハッシュマップで1種類の文字列のみを配置することを保証できるため、ハッシュを提供できますより高速であるか、または/および衝突が少ない関数。
- さまざまな目的のためにさまざまな種類のハッシュがあるため、ここでは汎用性はそれほど重要ではありません
- ほとんどの場合、より良いハッシュを作成することが可能です
でstd::swap
:
- 独自の swap 関数が必要になることはまずありませんが、
std::swap
コピー コンストラクターを呼び出す汎用関数ではなく、このクラスに固有の関数を使用することをお勧めします。
- ほとんどの場合、クラスの内部構造に関する知識が必要なため、swap 関数を作成することさえできません (たとえば、
std::vector
private フィールドとして非表示のポインターを持つ動的配列として実装できるため、それらにアクセスすることはできません)。 、それらを交換するだけでなく、そのように実装されているという事実でさえ保証されていません)
- スワップは 1 つだけです (またはあるはずです)。
- 実際には、問題があり
std::swap
ます。標準コンテナはswap
メンバー関数を提供し、std::swap
特殊化できます (ただし、テンプレート化されていないクラスのみ)。スワップは、ADL で見つかったフリー関数として定義できます。独自のスワップをどのように提供する必要がありますか? std::swap
関数でありstd::hash
ファンクターであるという事実ではなく、紛らわしいIMO 。
STL に一貫性がないのはなぜですか? ここでは推測することしかできませんが、STL が一貫していない主な理由は、(a) 互換性が不十分であり、(b) C++ もかなり一貫していないためです。