Какие еще языки поддерживают "частичную специализацию"?
Специализация частичного шаблона является одной из наиболее важных концепций для общего программирования в С++. Например: для реализации общей функции свопинга:
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 имеет дженерики, которые позволяют вам делать похожие вещи.