std::unique_lock がコンストラクタを区別するために型タグを使用するのはなぜですか? 質問する

std::unique_lock がコンストラクタを区別するために型タグを使用するのはなぜですか? 質問する

C++11では、std::unique_lockコンストラクタは、型タグdefer_lock_ttry_to_lock_t、を受け入れるようにオーバーロードされますadopt_lock_t

unique_lock( mutex_type& m, std::defer_lock_t t );
unique_lock( mutex_type& m, std::try_to_lock_t t );
unique_lock( mutex_type& m, std::adopt_lock_t t );

これらは空のクラス(型タグ)です以下のように定義される:

struct defer_lock_t { };
struct try_to_lock_t { };
struct adopt_lock_t { };

これにより、ユーザーは3つのコンストラクタのいずれかを渡すことで区別できるようになります。定義済みインスタンスこれらのクラス:

constexpr std::defer_lock_t defer_lock {};
constexpr std::try_to_lock_t try_to_lock {};
constexpr std::adopt_lock_t adopt_lock {};

これが として実装されていないことに驚きましたenum。私が知る限り、 を使用すると次enumのようになります。

  • 実装が簡単になる
  • 構文を変更しない
  • 実行時に引数を変更できるようにします (ただし、この場合はあまり役に立ちません)。
  • (おそらく)パフォーマンスに影響を与えずにコンパイラによってインライン化できる

標準ライブラリがenumこれらのコンストラクターの曖昧さを解消するために ではなく型タグを使用するのはなぜですか?おそらくもっと重要なのは、独自の C++ コードを書くときにも、このような状況で型タグを使用する方が良いかどうかです。

ベストアンサー1

タグディスパッチ

それはタグディスパッチングと呼ばれる技術クライアントに必要な動作に応じて適切なコンストラクターを呼び出すことができます。

タグの理由は、タグに使用されるタイプは無関係であるオーバーロードの解決中に競合することはありません。オーバーロードされた関数を解決するには、型 (列挙型の場合のように値ではありません) が使用されます。さらに、タグを使用して、あいまいな呼び出しを解決することもできます。この場合、タグは通常、何らかの型特性に基づきます。

テンプレートを使用したタグのディスパッチは、構造を考慮して使用する必要があるコードのみを実装する必要があることを意味します。

タグ ディスパッチにより、コードが読みやすくなり (少なくとも私の意見では)、ライブラリ コードがswitchシンプルになります。コンストラクターにはステートメントがなくなり、コンストラクター自体を実行する前に、これらの引数に基づいて初期化リストで不変条件を確立できます。もちろん、結果は人によって異なるかもしれませんが、これがタグを使用した私の一般的な経験です。

ブースト.orgタグディスパッチ技術に関する記事があります。その使用の歴史は古く、少なくともSGI STLに関しては

なぜそれを使うのですか?

標準ライブラリでは、これらのコンストラクターの曖昧さを解消するために、列挙型ではなく型タグを使用するのはなぜですか?

オーバーロードの解決や実装の際に使用される場合、型は列挙型よりも強力で柔軟です。列挙型は元々スコープがなく、使用方法が制限されていたことに注意してください (タグとは対照的)。

タグのその他の注目すべき理由。

  • どのコンストラクターを使用するかは実行時ではなくコンパイル時に決定できます。
  • 整数が、対応されていない値を持つ列挙型にキャストされる、より「ハッキーな」コードを禁止します。これを処理するための設計上の決定を行い、結果として生じる例外やエラーに対応するコードを実装する必要があります。
  • およびshared_lockでもlock_guardこれらのタグが使用されますが、 の場合はlock_guardのみadopt_lockが使用されます。列挙型を使用すると、エラー状態の可能性がさらに高まります。

優先順位と履歴もここで役割を果たしていると思います。標準ライブラリやその他の場所で広く使用されていることを考えると、元の例のような状況がライブラリで実装される方法が変わる可能性は低いでしょう。

おそらくもっと重要なのは、独自の C++ コードを書くときにも、このような状況で型タグを使用する方が良いかどうかです。

これは本質的に設計上の決定です。どちらも、解決する問題を対象に使用することができ、また使用する必要があります。特に、コンパイル時に実装に互換性がない場合や、オーバーロード解決が機能している場合、タグを使用してデータと型を正しい関数に「ルーティング」しました。

標準ライブラリは、std::advanceタグディスパッチを使用して、使用される型の特性 (または特徴) に基づいてアルゴリズムを実装および最適化する方法の例としてよく示されます (この場合、反復子はランダム アクセス反復子です)。

これは適切に使用すれば強力なテクニックなので、無視してはいけません。列挙型を使用する場合は、スコープのない古い列挙型よりも、スコープのある新しい列挙型を優先してください。

おすすめ記事