51

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

これを実現する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);
}

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

4

4 に答える 4

17

タグのディスパッチを希望します。理由は次のとおりです。

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

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

于 2011-08-03T13:00:21.127 に答える
16

私が好きな単純なコンパイル時ディスパッチのタグと部分的な特殊化の読みやすい代替手段は次のとおりです。[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);
于 2011-08-02T19:17:29.297 に答える
12

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

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

  • タグの唯一の目的は区別することであるため、タグをインスタンス化する必要はありません。
  • タグに基づいて新しいタイプと定数を簡単に定義できます
  • 引数はインターフェースで逆にすることができます。つまりdistance(point, polygon);distance(polygon, point);両方とも1つの実装のみを持つことができます。
于 2013-10-07T10:15:46.300 に答える
2

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

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)
{
}
于 2015-10-21T19:35:49.327 に答える