Share via


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

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]

Comments

  • Anonymous
    March 12, 2004
    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).

  • Anonymous
    March 12, 2004
    The comment has been removed

  • Anonymous
    March 12, 2004
    The comment has been removed

  • Anonymous
    March 12, 2004
    The comment has been removed

  • Anonymous
    March 13, 2004
    Hmmmm.

  • Anonymous
    March 13, 2004
    The comment has been removed

  • Anonymous
    March 13, 2004
    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.

  • Anonymous
    March 14, 2004
    Thoughts on generics

  • Anonymous
    March 14, 2004
    The comment has been removed

  • Anonymous
    March 15, 2004
    I've just write a trick about operators and generics in my weblog. Maybe you want to read it and send your opinions.

  • Anonymous
    March 17, 2004
    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

  • Anonymous
    March 17, 2004
    The comment has been removed

  • Anonymous
    March 25, 2004
    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.

  • Anonymous
    April 01, 2004
    one thing I didn't make clear in my previous comment is that constraints are useful to the implementation of things like generic sorted list or generic binary tree because it only makes sense to put something sortable into such data structures. and constraints (IComparable) are the way to guaranttee that you only get a strongly typed sorted list of a particular sortable (comparable) item. anything non-sortable (doesn't implement IComparable) cannot be used as the base type for a sorted list.

  • Anonymous
    April 27, 2004
    The comment has been removed

  • Anonymous
    December 03, 2007
    I came across an interesting quote from Tony Northrup regarding .net Generics: “I haven’t been able to

  • Anonymous
    December 03, 2007
    I came across an interesting quote from Tony Northrup regarding .net Generics: “I haven’t been able to

  • Anonymous
    June 07, 2009
    PingBack from http://greenteafatburner.info/story.php?id=2982

  • Anonymous
    June 08, 2009
    PingBack from http://forums.ngemu.com/web-development-programming/123240-your-way-saving-changes-c-application.html#post1669911

  • Anonymous
    June 08, 2009
    PingBack from http://quickdietsite.info/story.php?id=5467

  • Anonymous
    September 27, 2011
    download-mobile-software.blogspot.com

  • Anonymous
    November 29, 2011
    The difference can be demonstrated in the following C# codes: class B {     public String G() { return "B.G()"; } } class D : B {     public String G() { return "D.G()"; } }   class TestCompile {     private static String TestG<T>(T b) where T: B {          return b.G();     }     static void Main(string[] args) {         TestG(new D());     } } The result is B.G(), whereas the result of similar C++ code would be D.G().  It seems that "where T: B" instantiates TestG<B> and applies it everywhere.  If this is true, generic is quite less useful than template.