C# generics vs C++ templates - need a clarification about constraints

Well, in general, C++ templates and C# generics are similar - compared to Java generics which are completely different, but they have also large differences. Like in C#, there is runtime support by using reflection, getting an object describing the types used to instantiate a generics. C++ doesn't have reflection, and all it does with types is done at compile time.

The biggest difference between C# generics and C++ templates indeed are that C# generics are better type checked. They are always constrained, in the sense that they don't allow operations that are not stated valid at the time of defining the generics. C#'s chief designer raised as a reason of that the added complexity it would have taken to have implied constraints. I'm not well versed with C#, so i can't talk further here. I'll talk about about how matters are in C++ and how they are going to be improved, so that people don't think C++'s stuff is all wrong.

In C++, templates are not constrained. If you do an operation, at template definition time it is implied that the operation will succeed at instantiation time. It's not even required to a C++ compiler that the template is syntactically checked for validity. If it contains a syntax error, then that error has to be diagnosed at instantiation. Any diagnose before that is a pure goody of the implementation.

Those implied constraint have shown to be easy for the template designer in the short term, because they don't have to care about stating the valid operations in their template interface. They put the burden on the user of their template - so the user has to make sure he fulfills all those requirements. Often it happens that the user tries seemingly valid operations but fails, with the compiler giving the user hundreds of lines of error messages about some invalid syntax or not found names. Because the compiler can't know what constraint in particular was violated in the first place, it lists all parts of code paths ever involved around the faulty place and all not even important details, and the user will have to crawl through the horrible error message text.

That is a fundamental problem, which can be solved by just stating at the interface for a template or generics what properties a type parameter has to have. C#, as far as i know it, can constraint the parameter to implement an interface or inherit a base-class. It solves that on a type-level.

The C++ committee has long seen there is need to fix these problems, and soon (next year, probably), C++ will have a way to state such explicit constraints too (see time-machine note below), as in the following case.

template<typename T> requires VariableType<T>
T f(T a, T b) {
    return a + b; 
}

The compiler signals an error at that point, because the expression as written is not marked valid by the requirements. This first helps the designer of the template to write more correct code, because the code is type-checked already to some degree (well to what is possible there). The programmer can now state that requirement:

template<typename T> requires VariableType<T> && HasPlus<T, T>
T f(T a, T b) {
    return a + b; 
}

Now, it will compiler. The compiler, by seeing T appearing as the return type, automatically implied that T is copyable, because that use of T appears in the interface, rather than in the templates body. The other requirements were stated using requirement clauses. Now, the user will get a appropriate error message if he uses a type that doesn't have an op+ defined.

C++1x decouples the requirements from the type. The above works for primitive types aswell as for classes. In this sense, they are more flexible, but quite a bit complex. The rules that state when and when requirements are satisfied are long... You can with the new rules say the following:

template<typename T> requires MyCuteType<T>
void f(T t) { *t = 10; }

And then, call f with an int! That would work by just writing a concept map for MyCuteType<int> that teaches the compiler how an int can be dereferenced. It will get quite handy in loops like this:

for_each(0, 100, doSomething());

Since the programmer can tell the compiler how an int can satisfy the concept of an input iterator, you could actually write such code in C++1x, if you only write the appropriate concept map, which really isn't all that difficult.

Ok, enough with this. I hope i could show you that having templates constrained is not all that bad, but in fact better, because the relationship betweens types and the operations on them within the templates are now known by the compiler. And i haven't even written about axioms, which are another nice thing in C++1x' concepts. Remember that this is future stuff, it's not yet out, but it will approximately at 2010. Then we will have to wait for some compiler to implement that all :)


UPDATE FROM "FUTURE"

C++0x concepts were not accepted into the draft but have been voted out at late of 2009. Too bad! But perhaps we will see it again in the next C++ version? Let's all hope!


C++ templates: The compiler checks whether the arguments satisfy the constraints set by the code. For example:

template <typename T, unsigned int dim>
class math_vector
{
    T elements[dim];

    math_vector<T,dim> operator+ (const math_vector<T,dim>& other) const
    {
        math_vector<T,dim> result;
        for (unsigned int i = 0; i < dim; ++i)
            result.elements[i] = elements[i] + other.elements[i];
    }
}

struct employee
{
    char name[100];
    int age;
    float salary;
}

math_vector<int, 3> int_vec; //legal
math_vector<float, 5> float_vec; //legal
math_vector<employee, 10> employee_vec; //illegal, operator+ not defined for employee

In this example, you could create a class, define operator+ for it and use it as a parameter for math_vector. Therefore, a template parameter is valid if and only if it satisfies the constraints defined by the template's code. This is very flexible, but results in long compilation times (whether a type satisfies the template's constraints must be checked every time the template is instantiated).

C# generics: Instead of checking the validity of every particular instantiation, which results in longer compile times and is error prone, you declare explicitly that the generic's arguments must implement a particular interface (a set of methods, properties and operators). Inside the generic's code, you can't call any methods freely, but only those supported by that interface. Every time you instantiate a generic, the runtime doesn't have to check whether the argument satisfies a long set of constraints, but only whether it implements the specified interface. Of course, this is less flexible, but it's less error prone, too. Example:

class SortedList<T> where T : IComparable<T>
{
    void Add(T i) { /* ... */ }
}

class A : IComparable<A> { /* ... */ }

class B
{
    int CompareTo(B b) { /* ... */ }
    bool Equals(B b) { /* ... */ }
}

SortedList<A> sortedA; // legal
SortedList<B> sortedB; // illegal
// B implements the methods and properties defined in IComparable,
// however, B doesn't explicitly implement IComparable<B>

You'll get a better answer shortly, I'm sure. At that time, I'll delete this one.

The difference is that templates in C++ are similar to macros. It's when the template is instantiated that the code is compiled, and compilation errors are displayed if the implicit constraints are violated. That's how you can do template specializations - the template is basically already expanded by the specialization, so that's the one that's used.

Generics in .NET (also in VB.NET) are a runtime construct. They're a special kind of type. The constraints are necessary in order to ensure that any actual use of the type will be valid when the type is finally used.

You can actually use Reflection to look at a generic type and find the type parameters used to instantiate it, or look at a generic definition and see the constraints on each type parameter. In C++, this information is already gone at runtime.