The two biggest things that help metaprogramming templates in D are template limitations and static if - both of which C ++ can theoretically add and which will be very useful.
Template constraints allow you to set a condition for a template that must be true in order for the template to be created. For example, this is the signature of one of the std.algorithm.find overloads:
R find(alias pred = "a == b", R, E)(R haystack, E needle) if (isInputRange!R && is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
In order for this templated function to be created, the type R must be an input range, as defined by std.range.isInputRange (therefore isInputRange!R must be true ), and this predicate must be a binary function that compiles with these arguments and returns a type, which is implicitly converted to bool . If the result of the condition in the template is false , then the template will not compile. This not only protects you from the unpleasant template errors that you get in C ++ when templates will not compile with their given arguments, but it also makes it possible for you to overload templates based on the limitations of their templates. For example, there is another find overload, which is
R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if (isForwardRange!R1 && isForwardRange!R2 && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) && !isRandomAccessRange!R1)
It accepts exactly the same arguments, but its limitation is different. Thus, different types work with different overloads of the same templated function, and the best find implementation can be used for each type. In C ++ there is no way to do something clean. With a little familiarity with the functions and templates used in your typical template constraint, the template constraints in D are pretty easy to read, whereas you need very complicated metaprogramming of templates in C ++ to even try something like this that your average programmer doesn't to be able to understand, not to mention doing on their own. Boost is a prime example of this. It does some amazing things, but it is incredibly difficult.
static if improves the situation even more. As with the limitations of the template, you can use any condition with it that can be evaluated at compile time. eg.
static if(isIntegral!T) { //... } else static if(isFloatingPoint!T) { //... } else static if(isSomeString!T) { //... } else static if(isDynamicArray!T) { //... } else { //... }
Which branch compiles depending on which condition is first evaluated as true . Thus, in a template, you can specialize fragments of your implementation based on the types with which the template was created, or on the basis of anything else that can be evaluated at compile time. For example, core.time uses
static if(is(typeof(clock_gettime)))
to compile the code differently depending on whether the system clock_gettime or not (if clock_gettime exists, it uses it, otherwise it uses gettimeofday ).
Probably the most striking example I've seen where D improves on templates is the problem that my team at work ran into with C ++. We needed to create an instance of the template in different ways depending on whether the type that it was specified was obtained from a specific base class or not. We ended up using a solution based on this stack overflow question . It works, but it's quite complicated to just check if one type is derived from another.
In D, however, all you have to do is use the : operator. eg.
auto func(T : U)(T val) {...}
If T implicitly converted to U (as it would be if T were obtained from U ), then func will compile, and if T implicitly converted to U , then it will not. This simple improvement makes even the basic template specializations much more powerful (even without template restrictions or static if ).
Personally, I rarely use templates in C ++ except containers, and a random function in <algorithm> , because they are so sick to use. They lead to ugly mistakes and it is very difficult to do something interesting. To make something even a little complicated, you need to be very experienced with templates and template metaprogramming. With templates in D, it's so simple that I use them all the time. Errors are much easier to understand and handle (although they are still worse than errors, usually with non-template functions), and I do not need to figure out how to get the language to do what I want with fantastic metaprogramming.
There is no reason C ++ could not get most of these capabilities that D (C ++ concepts would help if they ever sorted them), but until they add basic conditional compilation with constructs similar to template constraints, and static if for C ++ C ++ templates simply cannot compare with D-templates in terms of ease of use and power.