Well, technically,
struct S(T) { ... }
equivalently
template S(T) { struct S { ... } }
and
auto foo(T)(T t) { ... }
equivalently
template foo(T) { auto foo(T t) { ... } }
It is just that a shorter syntax is provided to make things cleaner for general use. template creates a template for generating code. Nothing in this template exists as real code until the template is created using arguments, and the generated code depends on the template arguments. Thus, semantic analysis for the template is not performed until it is created.
Part of what happens to structures, classes, and functions that have a template as part of their declaration, instead of explicitly declaring a template to carry them, is called the so-called template of the same name. Any template that has a symbol in it with the same name as the template is replaced with this symbol when using the template. eg.
template isInt(T) { enum isInt = is(T == int); }
can then be used in an expression such as
auto foo = isInt!int;
and enum isInt.isInt used in the expression instead of the template. This method is used heavily with helper patterns for pattern constraints. e.g. isInputRange in
auto foo(R)(R range) if(isInputRange!R) {...}
isInputRange defined as the template of the same name, the result of which is true if this type is an input range and false otherwise. In some ways, this is similar to a function for working with types, although it can also work with values, and the result should not be bool . eg.
template count(Args...) { enum count = Args.length; }
or
template ArrayOf(T) { alias ArrayOf = T[]; }
There is also shortcut syntax for templates of the same name that are not user-defined types or functions if they have no other members. eg.
enum count(Args...) = Args.length; alias ArrayOf(T) = T[];
And, as I said, the template of the same name can be similar to a function for working with a type and that they are used when complex operations need to be performed by type. For example, using this ArrayOf template with std.meta.staticMap , you can do something like
alias Arrays = staticMap!(ArrayOf, int, float, byte, bool); static assert(is(Arrays == AliasSeq!(int[], float[], byte[], bool[])));
This can be extremely useful in template constraints (or other templates of the same name that will be used in template constraints), or it can be used with something like static foreach to more clearly generate code. for example, if I wanted to test some code with all types of strings, I could write something like
alias Arrays(T) = AliasSeq!(T[], const(T)[], const(T[]), immutable(T)[], immutable(T[])); unittest { import std.conv : to; static foreach(S; AliasSeq(Arrays!char, Arrays!wchar, Arrays!dchar)) {{ auto s = to!S("foo"); ... }} }
These methods are usually quite widely used in metaprogramming. They can be used for values ββin addition to types, but it is usually more efficient to use CTFE for values ββrather than putting them in AliasSeq and using different templates of the same name on them. For example, many years ago, Phobos had the Format template of the same name, which was used to generate strings at compile time in the same way as std.format.format does at run time, but as soon as CTFE was improved to such an extent that Format could be used at compile time, it made no sense to use Format instead of Format , because Format was terribly slow in comparison (executing many recursive template instances can be expensive). Thus, the use of template metaprogramming is still required when working with types, but if you can do what you need to do with CTFE, this is generally better.
If you are looking for metaprogramming tools in Phobos, std.traits and std.meta are the main place to search, although some of them are scattered throughout Phobos depending on what they are intended for (for example, for specific ranges in std.range.primitives )
Also, just like you can mix strings, you can mix patterns. eg
template foo(T) { T i; } void main() { mixin foo!int; auto a = i; }
Thus, the code created by the template is essentially copied in the form in which you mix it. Optionally, you can put mixin in a template declaration to make it illegal, to use it as something other than mixin. eg.
mixin template foo(T) { T i; }
Personally, I usually use string mixins for this kind of thing, but some people prefer mixins templates.