Какие еще языки поддерживают "частичную специализацию"?

Специализация частичного шаблона является одной из наиболее важных концепций для общего программирования в С++. Например: для реализации общей функции свопинга:

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

Специализировать его для вектора поддержки O (1) swap:

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 (ничего) в общей функции:

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

и получить наиболее подходящую реализацию во время компиляции. Это единственный способ для С++ достичь абстракции с минимальным штрафом за производительность.

Надеюсь, что это поможет разобраться в концепции "частичной специализации". В некотором смысле, это то, как С++ выполняет сопоставление шаблонов, не требуя явного синтаксиса сопоставления шаблонов (например, ключевое слово match в Ocaml/F #), которое иногда мешает универсальному программированию.

Ответ 1

D поддерживает частичную специализацию:

(сканирование "частичного" в приведенных выше ссылках).

Вторая ссылка, в частности, даст вам очень подробное описание того, что вы можете сделать с помощью специализированной специализации не только в D, но и на С++.

Здесь приведен конкретный пример swap. Он должен распечатать сообщение для 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;
}

Ответ 2

Я боюсь, что С# не поддерживает частичную специализацию шаблонов.

Специализация частичного шаблона означает:

У вас есть базовый класс с двумя или более шаблонами (generics/type parameters). Параметры типа были бы < T, S >

В производном (специализированном) классе указывается тип одного из параметров типа. Параметры типа могут выглядеть так: < T, int > .

Итак, когда кто-то использует (создает экземпляр объекта) класса, где последним параметром типа является int, используется производный класс.

Ответ 3

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.

Ответ 4

Собственно, вы можете (не совсем, см. ниже) сделать это в С# с помощью методов расширения:

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();

Ответ 5

С#:

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)

Ответ 6

Java имеет дженерики, которые позволяют вам делать похожие вещи.