web-dev-qa-db-ja.com

透明なコンパレータとは何ですか?

C++ 14では、連想コンテナはC++ 11から変更されたようです– [associative.reqmts]/13によると:

メンバー関数テンプレートfindcountlower_boundupper_bound、およびequal_rangeは、タイプCompare::is_transparentでない限り、オーバーロード解決に関与しません。存在します。

コンパレータを「透明」にする目的は何ですか?

C++ 14は、次のようなライブラリテンプレートも提供します。

template <class T = void> struct less {
    constexpr bool operator()(const T& x, const T& y) const;
    typedef T first_argument_type;
    typedef T second_argument_type;
    typedef bool result_type;
};

template <> struct less<void> {
    template <class T, class U> auto operator()(T&& t, U&& u) const
    -> decltype(std::forward<T>(t) < std::forward<U>(u));
    typedef *unspecified* is_transparent;
};

したがって、たとえば、std::set<T, std::less<T>>notには透明なコンパレータがありますが、std::set<T, std::less<>>1つあります。

これはどのような問題を解決し、標準のコンテナの動作を変更しますか?たとえば、std::setのテンプレートパラメーターはKey, Compare = std::less<Key>, ...のままなので、デフォルトセットはfindcountなどのメンバーを失いますか?

99
Kerrek SB

これはどのような問題を解決しますか、

Dietmarの答え および remyabelの答え を参照してください。

また、これにより標準コンテナの動作が変わりますか?

いいえ、デフォルトではありません。

findなどの新しいメンバー関数テンプレートオーバーロードにより、キータイプ自体を使用する代わりに、コンテナのキーに匹敵するタイプを使用できます。 N3465 byJoaquínMªLópezMuñozの理論的根拠と、この機能を追加するための詳細かつ慎重に書かれた提案を参照してください。

ブリストルの会議で、LWGは、異種検索機能が有用で望ましいことに同意しましたが、ホアキンの提案がすべての場合に安全であるとは確信できませんでした。 N3465の提案は、一部のプログラムで深刻な問題を引き起こしていました(既存のコードへの影響セクションを参照)。ホアキンは、トレードオフが異なるいくつかの代替実装を含む更新された提案案を作成しました。これは、LWGが長所と短所を理解するのに非常に役立ちましたが、何らかの形でいくつかのプログラムを破るリスクがあったため、この機能を追加するコンセンサスはありませんでした。機能を無条件に追加することは安全ではありませんが、デフォルトで無効にされ、「オプトイン」のみされている場合は安全であると判断しました。

N3657 プロポーザル( N3465 およびJoaquínによる後日未公開のドラフトに基づく私とSTLによる最後の改訂でした)の主な違いは、is_transparent新しい機能をオプトインするために使用できるプロトコルとして入力します。

「トランスペアレントファンクタ」(つまり、is_transparentタイプを定義するもの)を使用しない場合、コンテナは従来と同じように動作し、それがデフォルトのままです。

std::less<>(C++ 14の新機能)または別の「透明ファンクター」タイプを使用することを選択した場合、新しい機能が得られます。

エイリアステンプレートを使用すると、std::less<>を簡単に使用できます。

template<typename T, typename Cmp = std::less<>, typename Alloc = std::allocator<T>>
  using set = std::set<T, Cmp, Alloc>;

名前is_transparentは、STLの N3421 に由来し、C++ 14に「ダイヤモンド演算子」を追加しました。 「透明なファンクター」とは、引数のタイプ(同じである必要はない)を受け入れ、それらの引数を別の演算子に単純に転送するものです。このようなファンクターは、たまたま連想コンテナでの異種検索に必要なものであるため、すべてのダイアモンド演算子にis_transparent型が追加され、連想コンテナで新しい機能を有効にする必要があることを示すタグタイプとして使用されました。技術的には、コンテナには「トランスペアレントファンクタ」は必要ありません。異種タイプ(たとえば、 https://stackoverflow.com/a/18940595/981959のpointer_compタイプ はSTLの定義に従って透過的ではありませんが、pointer_comp::is_transparentを定義することで問題の解決に使用できます)。タイプTまたはintのキーを使用してstd::set<T, C>のみを検索する場合、CTおよびint(どちらの順序でも)、真に透明である必要はありません。より良い名前を思い付かなかったため、その名前を一部使用しました(そのようなファンクターは静的多型を使用するため、is_polymorphicを好んだでしょうが、動的多型を参照するstd::is_polymorphic型の特性が既にあります) 。

56
Jonathan Wakely

C++ 11には、メンバテンプレートfind()lower_bound()などはありません。つまり、この変更によって何も失われません。連想コンテナで異種キーを使用できるように、n3657でメンバーテンプレートが導入されました。良い例と悪い例を除いて、これが役立つ具体的な例は見当たりません!

_is_transparent_の使用は、不要な変換を回避することを目的としています。メンバテンプレートに制約がない場合、既存のコードは、メンバテンプレートなしで変換されたオブジェクトを直接通過する可能性があります。 n3657の使用例では、文字列リテラルを使用して_std::set<std::string>_内のオブジェクトを検索しています。C++ 11定義では、対応するメンバー関数に文字列リテラルを渡すときに_std::string_オブジェクトが構築されます。変更により、文字列リテラルを直接使用することができます。基礎となる比較関数オブジェクトが_std::string_の観点でのみ実装されている場合、比較ごとに_std::string_が作成されるため、これは悪いことです。一方、基礎となる比較関数オブジェクトが_std::string_および文字列リテラルを取ることができる場合、一時オブジェクトの構築を回避できます。

比較関数オブジェクトのネストされた_is_transparent_型は、テンプレートメンバ関数を使用するかどうかを指定する方法を提供します。比較関数オブジェクトが異種の引数を処理できる場合、異なる型を処理できることを示すためにこの型を定義します引数を効率的に。たとえば、新しい演算子関数オブジェクトはoperator<()に委任し、透明であると主張します。これは、少なくとも_std::string_を引数として使用する演算子よりもオーバーロードされていない_char const*_に対して機能します。これらの関数オブジェクトも新しいので、たとえ間違ったこと(つまり、あるタイプの変換が必要な場合)を行っても、少なくともパフォーマンスの低下をもたらすサイレントな変更ではありません。

32
Dietmar Kühl

以下は、 n3657 からのすべてのコピーパスタです。

Q.コンパレータを「透明」にする目的は何ですか?

A.連想コンテナルックアップ関数(find、lower_bound、upper_bound、equal_range)はkey_typeの引数のみを取り、ユーザーがルックアップを行うためにkey_typeのオブジェクトを(暗黙的または明示的に)構築する必要があります。これは高価な場合があります。コンパレーター関数がオブジェクトの1つのフィールドのみを参照する場合に、セット内で検索する大きなオブジェクトを作成します。ユーザーには、key_typeに匹敵する他のタイプを使用して検索できることが強く望まれています。

Q.これはどの問題を解決しますか

A. LWGには、次のようなコードに関する懸念がありました。

std::set<std::string> s = /* ... */;
s.find("key");

C++ 11では、これは単一のstd :: string temporaryを構築し、それを要素と比較してキーを見つけます。

N3465によって提案された変更により、std :: set :: find()関数は、const char *をコンパレータ関数std :: lessに渡す制約のないテンプレートになり、std :: stringを一時的に構築しますすべての比較。 LWGは、このパフォーマンスの問題を深刻な問題と見なしました。テンプレートのfind()関数は、ポインターのコンテナー内でNULLを検出することも防止します。これにより、以前は有効だったコードがコンパイルされなくなりますが、これはパフォーマンスのサイレント回帰ほど深刻ではない問題と見なされていました

Q.この変更は標準コンテナの動作方法を変更します

A.この提案では、ルックアップメンバー関数をメンバー関数テンプレートでオーバーロードすることにより、関連コンテナーを変更します。言語の変更はありません。

Q.デフォルトセットは検索、カウントなどを失いますか?members

A.新しいC++ 14ライブラリ機能を比較関数として使用しない限り、メンバー関数は存在しないため、既存のC++ 11コードのほとんどは影響を受けません。

Yakk を引用するには、

C++ 14では、Compare :: is_transparentが存在する場合、std :: set :: findはテンプレート関数です。渡す型はキーである必要はなく、コンパレーターの下でちょうど同等です。

およびn3657、

23.2.4 [associative.reqmts]の段落13を追加します。メンバー関数テンプレートfind、lower_bound、upper_bound、equal_rangeは、型Compare :: is_transparentがなければオーバーロード解決に関与しません。 存在しない ある。

n3421"Transparent Operator Functors" の例を提供します。

完全なコードはこちら

18
user1508519

Stephan T Lavavejが、コンパイラが一時的なものを作成し続ける問題と、透明演算子ファンクターの彼の提案がc ++ 1yでどのようにこれを解決するかについて話します

GoingNative 2013-コンパイラを助けてはいけない (約1時間で)

6
woolstar