Частичная специализация шаблонов - одна из важнейших концепций универсального программирования на C++. Например: для реализации общей функции подкачки:
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 (что угодно) в общей функции:
template <typename T>
void bar(T x) {
// stuff...
foo(x);
// more stuff...
}
и получите наиболее подходящую реализацию во время компиляции. Это один из способов для C++ добиться абстракции с минимальным снижением производительности.
Надеюсь, это поможет прояснить понятие «частичная специализация». В некотором смысле, именно так C++ выполняет сопоставление с образцом типов без необходимости явного синтаксиса сопоставления с образцом (скажем, ключевого слова match в Ocaml / F#), что иногда мешает универсальному программированию.





В Java есть дженерики, которые позволяют делать подобные вещи.
Извините, дженерики Java уродливы и даже близко не подходят для чего-то подобного.
ну, в java вы можете реализовать своп за время O (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)
Но здесь нет частичной специализации.
Своп не имеет смысла в Haskell, потому что переменные неизменяемы. Можете ли вы привести еще один пример, который имеет смысл на других языках? Общий своп в Lisp будет использовать макрос: (defmacro swap (x y) `(let ((z, x)) (setf, x, y) (setf, y, z)))
Боюсь, что C# не поддерживает частичную специализацию шаблонов.
Частичная специализация шаблона означает:
У вас есть базовый класс с двумя или более шаблонами (общие / типовые параметры). Параметры типа будут <T, S>
В производном (специализированном) классе вы указываете тип одного из параметров типа. Параметры типа могут выглядеть так: <T, int>.
Поэтому, когда кто-то использует (создает экземпляр объекта) класса, в котором последним параметром типа является int, используется производный класс.
Не могли бы вы ограничить S в производном классе, указав где S: int?
Нет; вам нужно сделать так, чтобы базовый класс времени любой создавался с помощью S == int, вместо этого создается экземпляр производного класса.
В Perl это просто ($ x, $ y) = ($ y, $ x);
В 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.
Собственно, вы можете (не совсем; см. Ниже) сделать это на 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();
Версия C++ также решается во время компиляции, так что это не так уж и далеко. Проблема начинается, когда вы вызываете Count из общего класса, а компилятор не знает, для какого типа вы его вызываете. C++ здесь работает лучше, потому что он откладывает решение до тех пор, пока не узнает все типы.
D поддерживает частичную специализацию:
(просканируйте "частичное" в приведенных выше ссылках).
Вторая ссылка, в частности, предоставит вам подробное описание очень того, что вы можете делать со специализацией шаблонов, не только в D, но и в C++.
Вот конкретный пример swap D. Он должен распечатать сообщение для свопа, специализированного для класса 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;
}
Да, D определенно поддерживает это. Спасибо за пример. Кстати, вы не думаете, что swap (T: Thing! (U), U) (...) действительно должен быть swap (T: Thing! (U)) (...). Дополнительная буква «U» кажется неуместной.
Да, ты прав. Я вырезал и вставил его из исходного файла, прежде чем моя компиляция была завершена (состояние гонки в реальном мире: P). Фиксированный.
Боюсь, что ни одна из перечисленных вами функций не может считаться частичной специализацией, поскольку вы не можете частично специализировать шаблонные функции, а только классы. Честно говоря, это возникает только при рассмотрении попытки расширить пространство имен std для определяемых пользователем шаблонных типов. (Вам разрешено специализироваться, но не перегружать материал в std.) См. Эффективный элемент C++ 25 или эту статью GotW gotw.ca/publications/mill17.htm. Отбросьте это, и это просто вопрос о правилах перегрузки функций и поиска.