How do C# generics compare to C++ templates?

How do C# generics compare to C++ templates?

Rate This
  • Comments 28

Q: How do C# generics compare to C++ templates?

A: This is really a fairly complex topic.

Anders has touched on it in an interview.

I should state at the outset that the goals of generics are not the same as the goals of templates. There are some things that templates do better than generics, and vice versa.

Model

C++ templates use a compile-time model. When a template is used in a C++ program, the effect is as if a sophisticated macro processor had been used.

C# generics are not just a feature of the compiler, but also a feature of the runtime. A generic type such as List<T> maintains its generic-ness (genericity) after it has been compiled. Or, to look at it another way, the substitution that the C++ compiler does at compile time is done at JIT time in the C# generic world.

Error Checking

Error is best illustrated through an example. Consider a template that has a method like this;

T Add(T t1, Tt2)
{
    return t1 + t2;
}

the C++ compiler will be able to parse this method successfully. When this template is actually used, the Ts will be replaced with an actual type. If a type such as int is used, the compiler will be able to create the Add method correctly, as it knows how to add two ints.

If a type such as Employee was used, the compiler would issue an error, as the compiler would know that there was no way to add two Employees.

The generics world is very different. Because the type that is used with the generic is not known at compile time, the compiler needs additional information about the type that will be used with a generic class.

This is done through constraints, which allow the author to constrain the types that can be used with a generic type.

For example:

Class List<T> where T:IComparable

means that whenever I use a T in my implementation, I can call the CompareTo() function on it.

Constraints could theoretically provide nearly the same level of flexibility that templates do, but that would require a very complex constraint syntax. For the Whidbey release, only certain operations can be specified through contraints, which limits the number of operations you can perform.

For example, there is no way to say that a generic type must have an add operator, so you can't write “a + b“ in a generic class.

It is possible to work around this at runtime using reflection, but the implementation isn't as clean and there may be a performance loss. We may address some of these issues in future releases

Run-time operations

Generics in C# have full run-time support. If you use reflection, you will find that you can reflect over generic types, and create them at runtime. There's no real analog of this in the C++ world.

Space Use

The use of space is different between C++ and C#.  Because C++ templates are done at compile time, each use of a different type in a template results in a separate chunk of code being created by the compiler.

In the C# world, it's somewhat different. The actual implementations using a specific type are created at runtime. When the runtime creates a type like List<int>, the JIT will see if that has already been created. If it has, it merely users that code. If not, it will take the IL that the compiler generated and do appropriate replacements with the actual type.

That's not quite correct. There is a separate native code path for every value type, but since reference types are all reference-sized, they can share their implementation.

This means that the C# approach should have a smaller footprint on disk, and in memory, so that's an advantage for generics over C++ templates.

In fact, the C++ linker implements a feature known as “template folding“, where the linker looks for native code sections that are identical, and if it finds them, folds them together. So it's not a clear-cut as it would seem to be.

Template metaprogramming

C++ templates are sometimes used for a technique known as template metaprogramming. There is no way to do this in C#.

[Author: Eric Gunnerson]

Leave a Comment
  • Please add 5 and 2 and type the answer here:
  • Post
  • Why would you use generics if you limit your types to classes implementing a single interface? Why wouldn't you just use that interface directly? So instead of:

    Class List<T> where T:IComparable
    {
    void Add(T Item) {...};
    }

    you'd use:


    Class List
    {
    void Add(IComparable Item) {...};
    }

    The resulting effect is the same, it's faster, the code is easier to read and thanks to refactoring code it's not such a big deal to change your underlying interface.

    The only reason to use generics is when valid types do not share a common base class (read other then Object).
  • JIT is COMPILE TIME u twonk, just a during the running of the program it decides to "compile" "just in time". You mean BUILD TIME surely, not compile time.
  • C++ Templates are PREPROCESSOR TIME, not compile time, the preprocesssor takes care or them not the compiler, no?

    This is one thing I hate about Kludge++ (C++), is the mess of precompile substitution that you cant debug. The day you go there on the CLR is the day I dump the technology.
  • You can tell how bad Cludge++ (C++) is because of the fat that you need a template to hack in the abstraction of "properties" and these are only in the COM world "by design" for unmanaged code.
  • Hmmmm.
  • Jerry,

    I think there are two reasons generics work better than interfaces.

    First, it's hard to know up front what kind of types you might want to use.

    Second, interfaces require boxing for value types, and also introduce copy semantics where you don't want them.

    Third, generic methods allow you to do things you couldn't do before.

    Oh, and a bonus forth reason - if you go with the base class approach, you have to add casts to your code.
  • Eric, I was talking about generics with type constraints. I understand what generics are and can do and I use them (quite a lot) in my C++ code but I don't understand why the concept of type constraints was introduced, if you don't want to use generics but rather work with a single type you can just use interfaces.
  • Thoughts on generics
  • Jerry,

    You can use interfaces instead, but ther are two disadvantages.

    1) If you are ever using the underlying type rather than the interface, you'll have to cast from the interface back to the underlying type. Whether this is an issue depends on your use.

    2) Value types that are converted to interfaces are boxed.
  • I've just write a trick about operators and generics in my weblog. Maybe you want to read it and send your opinions.

  • There are a number of other generic programming techniques that you can do with C++ templates (and generics in other languages like Haskell), that you can't do with .NET or Java generics. See this paper for details:

    http://www.osl.iu.edu/publications/pubs/2003/comparing_generic_programming03.pdf
  • Jerry,

    main point here is the type safety.

    in your List example of adding with IComparable. I can basically cast anything that implements IComparable interface and try to add it to the list. during runtime, most likely when IComparable.CompareTo is called, some kind of checking will throw an exception if the two items I'm trying to compare are not of the same type (yet they both implement IComparable). that's no good.

    with generics, I can catch this at compile time, verify that whatever list I create will only accept 1 type and 1 type only. and with ability to specify constraint, I can implement something like a generic sorted list or binary tree routine that is strongly typed when I program.
  • I think everyone here has made some good points about the “generics with constraints” versus “interfaces” debate and like most equivalent tools there’s a time to use one over the other.

    It seems to me that you’d want to use “generics with constraints” when type safety is important (i.e. you want to make sure everything is the same type). However, sometimes this isn’t flexible enough (for example you want to be able to plug in different types at runtime) and that’s when you’d use interfaces.

    Granted, I’m sure you could manipulate generics to do the latter, but I don’t clearly see how this could be done without some sort of reflection, and that would make the code confusing.
Page 1 of 2 (28 items) 12