The examples for D while they will most likley compile, it's not really how one would write D templates today. I also provide usage examples showing that everything compiles as expected.
Set example:
// no need to use the template keyword
struct Set(T) if(is(typeof(T.init == T.init))) // Verify T has equlity
{
void add(T o) {}
}
Sum example. Since D supports operator overloading and +
does not necessarly need to be the same as +=
we're checking for +=
since that's what is used in the implementation.
enum hasAddAssign(T) = __traits(compiles, { T a; a += a; }); // Verify T supports +=
T sum(T)(T[] x) if (hasAddAssign!T) // only for arrays
{
T s;
foreach (e; x)
s += e;
return s;
}
The above example only supports arrays. Usually one would implement a function like sum
to take a range
instead of an array. The range version handles bascilly anything that is iteratable:
enum hasAddAssign(T) = __traits(compiles, { T a; a += a; }); // Verify T supports +=
import std.array : front;
import std.range : isInputRange, ElementType;
auto sum(Range)(Range x)
if (isInputRange!Range && hasAddAssign!(ElementType!Range))
{
ElementType!Range s;
foreach (e; x)
s += e;
return s;
}
ShortestPath example:
import std.traits : ReturnType;
enum isGraph(Node, Edge) =
is(ReturnType!(Node.Edges) == Edge[]) &&
is(ReturnType!(Edge.Nodes) == Node[]);
Edge[] shortestPath(Edge, Node)(Node src, Node dest) if (isGraph!(Node, Edge))
{
return null;
}
Usage:
struct Node
{
Edge[] Edges()
{
return null;
}
}
struct Edge
{
Node[] Nodes()
{
return null;
}
}
void main()
{
shortestPath!(Edge)(Node(), Node()); // implicit function template instantiation for the second type
[1, 2, 3].sum(); // implicit function template instantiation
// UFCS, Uniform Function Call Syntax. That is, a free function can be called like a member function
Set!int a; // template instantiations without parentheses if the type is a single symbol
a.add(3);
}