10

std::hashオーバーロードされた関数の解決中に行われる暗黙的な型変換を避けるために、テンプレート構造体として定義されていると思います。言うのは正しいことですか?

つまり、私は書くことを好むだろう

std::string s;
size_t hash = std::hash(s);

それ以外の

std::string s;
size_t hash = std::hash<std::string>()(s);

しかし、標準化委員会が 2 番目のオプションを選択したのには理由があると思います。

4

2 に答える 2

13

関数テンプレートを部分的に特殊化することは不可能であるため、ユーザー定義のテンプレート化されたクラスの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::vectorprivate フィールドとして非表示のポインターを持つ動的配列として実装できるため、それらにアクセスすることはできません)。 、それらを交換するだけでなく、そのように実装されているという事実でさえ保証されていません)
  • スワップは 1 つだけです (またはあるはずです)。
  • 実際には、問題がありstd::swapます。標準コンテナはswapメンバー関数を提供し、std::swap特殊化できます (ただし、テンプレート化されていないクラスのみ)。スワップは、ADL で見つかったフリー関数として定義できます。独自のスワップをどのように提供する必要がありますか? std::swap関数でありstd::hashファンクターであるという事実ではなく、紛らわしいIMO 。

STL に一貫性がないのはなぜですか? ここでは推測することしかできませんが、STL が一貫していない主な理由は、(a) 互換性が不十分であり、(b) C++ もかなり一貫していないためです。

于 2013-07-07T21:54:54.527 に答える
4

考えられる理由の 1 つは、このようにして、変更可能なオプションによってデフォルトとしてテンプレートで使用する方が簡単だからです。

template <typrname T, typename = std::hash<T>...>
class unordered_set;

ところで、このように機能する関数を作成することができます

template<typename T, typename... Args>
auto hasher(Args&&... args) -> whatever { 
    return std::hash<T>(std::forward<Args>(args)...)( //maybe some &'s  skipped
}

または(タイプの検出を許可するため)

template<typename T, typename... Args> 
auto hasher(T t) -> whatever {
    return std::hash<T>()(t);
}
于 2013-07-07T21:35:55.787 に答える