web-dev-qa-db-ja.com

部分的に特殊化されたクラスでのタグディスパッチと静的メソッド

ジェネリック関数void f<T>()を記述したいとします。これは、TがPODタイプの場合に1つのことを行い、Tが非POD(またはその他の任意の任意のもの)の場合に別のことを行います。述語)。

これを実現する1つの方法は、標準ライブラリがイテレータカテゴリで行うようにタグディスパッチパターンを使用することです。

template <bool> struct podness {};
typedef podness<true> pod_tag;
typedef podness<false> non_pod_tag;

template <typename T> void f2(T, pod_tag) { /* POD */ }
template <typename T> void f2(T, non_pod_tag) { /* non-POD */ }

template <typename T>
void f(T x)
{
    // Dispatch to f2 based on tag.
    f2(x, podness<std::is_pod<T>::value>());
}

別の方法は、部分的に特殊化されたタイプの静的メンバー関数を使用することです。

template <typename T, bool> struct f2;

template <typename T>
struct f2<T, true> { static void f(T) { /* POD */ } };

template <typename T>
struct f2<T, false> { static void f(T) { /* non-POD */ } };

template <typename T>
void f(T x)
{
    // Select the correct partially specialised type.
    f2<T, std::is_pod<T>::value>::f(x);
}

ある方法を他の方法よりも使用することの長所と短所は何ですか?どちらをお勧めしますか?

48
Peter Alexander

タグのディスパッチを希望する理由:

  • 新しいタグで簡単に拡張できます
  • 使いやすい継承(
  • ジェネリックプログラミングではかなり一般的な手法です

2番目の例で3番目のバリアントを追加するのは難しいようです。たとえば、POD以外のタイプを追加する場合は、template <typename T, bool> struct f2;boolを他のものに置き換える必要があります(必要に応じてint = ))そしてすべてのstruct f2<T, bool-value>struct f2<T, another-type-value>に置き換えます。そのため、私にとって2番目のバリアントはほとんど拡張できないように見えます。間違えたら訂正してください。

15
tim

読み取り可能 _[boost|std]::enable_if_の代替、タグ、および単純コンパイル時ディスパッチの部分的特殊化は次のとおりです。

[ブール値は整数に変換され、長さがゼロの配列は無効であり、問​​題のあるテンプレートは破棄されることに注意してください(SFINAE)。また、char (*)[n]n要素の配列へのポインタです。]

_template <typename T> 
void foo(T, char (*)[is_pod<T>::value] = 0)
{
    // POD
}

template <typename T> 
void foo(T, char (*)[!is_pod<T>::value] = 0)
{
    // Non POD
}
_

また、名前空間を汚染する外部クラスを必要としないという利点もあります。ここで、質問のように述語を外部化したい場合は、次のことができます。

_template <bool what, typename T>
void foo(T, char (*)[what] = 0)
{
    // taken when what is true
}

template <bool what, typename T>
void foo(T, char (*)[!what] = 0)
{
    // taken when what is false
}
_

使用法:

_foo<std::is_pod<T>::value>(some_variable);
_
15
Alexandre C.

実際には、どちらもタグディスパッチパターンのみです。前者はインスタンスによるタグディスパッチと呼ばれ、後者はタイプによるタグディスパッチと呼ばれます

Boost.Geometryの主な作者であるBarendは、 説明 両方の方法であり、後者を好みます。これはBoost.Geometryで広く使用されています。要約した利点は次のとおりです。

  • タグの唯一の目的は区別することであるため、タグをインスタンス化する必要はありません。
  • タグに基づいて新しいタイプと定数を簡単に定義できます
  • 引数はインターフェースで逆にすることができます。つまり、distance(point, polygon);distance(polygon, point);の両方に実装を1つだけ含めることができます。
10
legends2k

私はこれがすでに受け入れられた答えを持つ古い質問であることを知っていますが、これは実行可能な代替案かもしれません:

template<typename T>
std::enable_if_t<std::is_pod<T>::value> f(T pod)
{
}

template<typename T>
std::enable_if_t<!std::is_pod<T>::value> f(T non_pod)
{
}
1
Felics