11

部分的なテンプレートの特殊化は、C++ でのジェネリック プログラミングの最も重要な概念の 1 つです。例: 一般的な swap 関数を実装するには:

template <typename T>
void swap(T &x, T &y) {
  const T tmp = x;
  y = x;
  x = tmp;
}

O(1) スワップをサポートするベクトルに特化するには:

template <typename T, class Alloc>
void swap(vector<T, Alloc> &x, vector<T, Alloc> &y) { x.swap(y); }

そのため、汎用関数で swap(x, y) を呼び出すと、常に最適なパフォーマンスを得ることができます。

代替言語で同等のもの(または言語がスワップの概念をサポートしていない場合は、言語の部分的な特殊化の標準的な例)を投稿できれば、非常に感謝しています。

編集:したがって、回答/コメントした多くの人々は、部分的な特殊化が何であるかを本当に知らず、一般的なスワップの例が一部の人々の理解を妨げているようです。より一般的な例は次のとおりです。

template <typename T>
void foo(T x) { generic_foo(x); }

部分的な特殊化は次のようになります。

template <typename T>
void foo(vector<T> x) { partially_specialized_algo_for_vector(x); }

完全な専門化は次のようになります。

void foo(vector<bool> bitmap) { special_algo_for_bitmap(bitmap); }

なぜこれが重要なのですか?ジェネリック関数で foo(anything) を呼び出すことができるため:

template <typename T>
void bar(T x) {
  // stuff...
  foo(x);
  // more stuff...
}

コンパイル時に最も適切な実装を取得します。これは、C++ が最小限のパフォーマンス ペナルティで抽象化を実現する 1 つの方法です。

「部分専門化」の概念を明確にするのに役立つことを願っています。ある意味で、これは C++ が明示的なパターン マッチング構文 (Ocaml/F# の match キーワードなど) を必要とせずに型パターン マッチングを行う方法であり、ジェネリック プログラミングの邪魔になることがあります。

4

6 に答える 6

5

Dは部分的な特殊化をサポートしています。

(上記のリンクで「部分的」をスキャンしてください)。

特に 2 番目のリンクでは、D だけでなく C++ でも、テンプレートの特殊化で何ができるかについて非常に詳細な内訳が得られます。

の D 固有の例を次に示しswapます。Thingクラスに特化したスワップのメッセージを出力する必要があります。

import std.stdio;    // for writefln

// Class with swap method

class Thing(T)
{
public:

    this(T thing)
    {
        this.thing = thing;
    }

    // Implementation is the same as generic swap, but it will be called instead.
    void swap(Thing that)
    {
       const T tmp = this.thing;
       this.thing = that.thing;
       that.thing = tmp;
    }

public:

    T thing;
}


// Swap generic function

void swap(T)(ref T lhs, ref T rhs)
{
    writefln("Generic swap.");

    const T tmp = lhs;
    lhs = rhs;
    rhs = tmp;
}

void swap(T : Thing!(U))(ref T lhs, ref T rhs)
{
    writefln("Specialized swap method for Things.");

    lhs.swap(rhs);
}

// Test case

int main()
{
    auto v1 = new Thing!(int)(10);
    auto v2 = new Thing!(int)(20);

    assert (v1.thing == 10);
    assert (v2.thing == 20);
    swap(v1, v2);
    assert (v1.thing == 20);
    assert (v2.thing == 10);

    return 0;
}
于 2008-12-24T18:56:50.087 に答える
1

Haskellには拡張機能として重複するインスタンスがあります:

class Sizable a where
  size :: a -> Int

instance Collection c => Sizable c where
  size = length . toList

より具体的なインスタンスを持つことができる任意のコレクションのサイズを見つける関数です。

instance Sizable (Seq a) where
  size = Seq.length

HaskellWikiのAdvancedOverlapも参照してください。

于 2008-12-19T08:29:13.997 に答える
0

実際には、拡張メソッドを使用して C# で実行できます (完全ではありません。以下を参照)。

public Count (this IEnumerable<T> seq) {
    int n = 0;
    foreach (T t in seq)
        n++;
    return n;
}

public Count (this T[] arr) {
    return arr.Length;
}

次に、呼び出しarray.Count()は特殊化されたバージョンを使用します。array「完全ではない」というのは、解像度が実行時の型ではなく、の静的な型に依存するためです。つまり、これはより一般的なバージョンを使用します。

IEnumerable<int> array = SomethingThatReturnsAnArray();
return array.Count();
于 2008-12-19T08:54:15.020 に答える
-1

C#:

void Swap<T>(ref T a, ref T b) {   
  var c = a;   
  a = b;   
  b = c;
}

(純粋な) Haskell バージョンは次のようになると思います。

swap :: a -> b -> (b,a)
swap a b = (b, a)
于 2008-12-19T07:44:12.493 に答える
-3

Java にはジェネリクスがあり、同様のことを行うことができます。

于 2008-12-19T04:01:20.150 に答える