views:

374

answers:

6

Partial template specialization is one of the most important concepts for generic programming in C++. For example: to implement a generic swap function:

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

To specialize it for a vector to support O(1) swap:

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

So you can always get optimal performance when you call swap(x, y) in a generic function;

Much appreciated, if you can post the equivalent (or the canonical example of partial specialization of the language if the language doesn't support the swap concept) in alternative languages.

EDIT: so it looks like many people who answered/commented really don't known what partial specialization is, and that the generic swap example seems to get in the way of understanding by some people. A more general example would be:

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

A partial specialization would be:

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

A complete specialization would be:

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

Why this is important? because you can call foo(anything) in a generic function:

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

and get the most appropriate implementation at compile time. This is one way for C++ to achieve abstraction w/ minimal performance penalty.

Hope it helps clearing up the concept of "partial specialization". In a way, this is how C++ do type pattern matching without needing the explicit pattern matching syntax (say the match keyword in Ocaml/F#), which sometimes gets in the way for generic programming.

A: 

Java has generics, which allow you to do similar sorts of things.

Milhous
Sorry, Java generics is crippled and not even close to do something like that.
obecalp
well in java you can implement swap in O(1) time for any generic class.
Milhous
A: 

C#:

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

I guess the (pure) Haskell-version would be:

swap :: a -> b -> (b,a)
swap a b = (b, a)
finnsson
But there is no partial specialisation here.
Alexey Romanov
Swap doesn't make sense in Haskell because variables aren't mutable. Can you give another example that does make sense in other languages?A generic swap in Lisp would use a macro: (defmacro swap (x y) `(let ((z ,x)) (setf ,x ,y) (setf ,y ,z)))
Jules
+1  A: 

I am afraid that C# does not support partial template specialization.

Partial template specialization means:

You have a base class with two or more templates (generics / type parameters). The type parameters would be <T, S>

In a derived (specialized) class you indicate the type of one of the type parameters. The type parameters could look like this <T, int>.

So when someone uses (instantiates an object of) the class where the last type parameter is an int, the derived class is used.

khebbie
Couldn't you constrain S in the derived class by specifying where S : int?
Jon Limjap
No; you need to make it so that _any_ time base class is instantiated with `S == int`, the derived class is instantiated instead.
Alexey Romanov
It's simple in Perl ($x,$y) = ($y,$x);
Brad Gilbert
+1  A: 

Haskell has overlapping instances as an extension:

class Sizable a where
  size :: a -> Int

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

is a function to find size of any collection, which can have more specific instances:

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

See also Advanced Overlap on HaskellWiki.

Alexey Romanov
A: 

Actually, you can (not quite; see below) do it in C# with extension methods:

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;
}

Then calling array.Count() will use the specialised version. "Not quite" is because the resolution depends on the static type of array, not on the run-time type. I.e. this will use the more general version:

IEnumerable<int> array = SomethingThatReturnsAnArray();
return array.Count();
Alexey Romanov
The c++ version is also resolved at compile time, so this isn't too far off. The problem starts when you call Count from within a generic class and the compiler doesn't know what type you're calling it on. C++ does a better job there because it delays the decision until it knows all the types.
Daniel Earwicker
+3  A: 

D supports partial specialization:

(scan for "partial" in the above links).

The second link in particular will give you a very detailed breakdown of what you can do with template specialization, not only in D but in C++ as well.

Here's a D specific example of swap. It should print out the message for the swap specialized for the Thing class.

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;
}
quark
Yeah, D definitely supports it. Thanks for the example. BTW, don't you think swap(T: Thing!(U), U)(...) should really be swap(T: Thing!(U))(...). The additional ", U" seems out of place.
obecalp
Yes, you are right. I cut and paste it from a source file, before my compile was finished (real world race condition :P). Fixed.
quark