Report a bug
		
				If you spot a problem with this page, click here to create a Bugzilla issue.
		
			Improve this page
		
			Quickly fork, edit online, and submit a pull request for this page.
			Requires a signed-in GitHub account. This works well for small changes.
			If you'd like to make larger changes you may want to consider using
			a local clone.
		
	Template Comparison
C++ pioneered templates and template metaprogramming and has continued to improve on it. The D programming language is the first to comprehensively reengineer templates based on the C++ experience.
| Feature | D | C++ | 
|---|---|---|
| Argument list delineation | Uses !( ), as in Foo!(int). Can omit parens when the argument is a single lexical token: Foo!int | Uses < > as in Foo<int> | 
| Class Templates | Yes: class Foo(T)
{
    T x;
}
 | Yes: template<class T>
class Foo
{
    T x;
};
 | 
| Function Templates | Yes: T foo(T)(T i)
{
    ...
}
 | Yes: template<class T>
T foo(T i)
{
    ...
}
 | 
| Member Templates | Yes | Yes | 
| Constructor Templates | Yes | Yes | 
| Parameterize any Declaration | Yes, classes, functions, any
            alias,
            variables, any enum, etc. can be parameterized,
            such as this variable: template Foo(T) { static T* p; } | C++98 No, only classes and functions C++11 Added using type aliases: template<class T> using ptr = T*; ptr<int> p;C++14 Added constexpr constant: template<class T> constexpr T pi = T(3.1415926535897932385L); | 
| Template Typedefs: Create an alias that binds to some but not all of the template parameters | Yes: class Foo(T, U) { } template MyFoo(T) { alias MyFoo = Foo!(T, int); } MyFoo!(uint) f; | C++98 No C++11 Yes: template<class T, class U> class Foo { };
template<class T> using MyFoo = Foo<T, int>;
MyFoo<unsigned> f;
 | 
| Sequence Constructors | No, use a variadic parameter instead | C++98 No C++11 Yes: template <class T>
class Foo {
    Foo(std::initializer_list<T>);
};
Foo<double> f = { 1.2, 3.0, 6.8 };
 | 
| Concepts | Yes: Constraints | C++98 No C++20 Yes: Constraints and Concepts | 
| Recursive Templates | Yes: template factorial(int n) { const factorial = n * factorial!(n-1); } template factorial(int n : 1) { const factorial = 1; } | Yes: template<int n> class factorial
{
  public:
    enum { result = n * factorial<n-1>::result };
};
template<> class factorial<1>
{
  public:
    enum { result = 1 };
};
 | 
| Conditional Compilation based on Template Arguments | Yes: void foo(T)(T i) { static if (can_fast_foo!(T)) FastFoo f = fast_foo(i); else SlowFoo f = slow_foo(i); use_foo(f); } class HashTable(T, int maxLength) { static if (maxLength < 0xFFFE) alias CellIdx = ushort; else alias CellIdx = uint; CellIdx index; } | C++98 No, but workarounds exist: template<class T> void foo(T i)
{
  // Differentiate using a
  // Helper<bool> specialization
  Helper<can_fast_foo<T>>::use_foo(i);
};
template<class T, int maxLength> class HashTable {
    typedef typename std::conditional<
            maxLength < 0xFFFE, uint16_t, uint32_t>
        ::type CellIdx;
    CellIdx index;
};
C++17 Yes, but it's limited to block scope: template<class T> void foo(T i)
{
  if constexpr (can_fast_foo<T>)
    FastFoo foo = fast_foo(i);
  else
    SlowFoo foo = slow_foo(i);
  use_foo(foo); // error foo is undeclared
}
template<class T, int maxLength> class HashTable {
    // error cannot use 'if' outside of a function
    if constexpr (maxLength < 0xFFFE)
        using CellIdx = ushort;
    else
        using CellIdx = uint;
    CellIdx index;
};
 | 
| Template Forward Declarations | Not necessary | Yes: template<class T> class Foo; | 
| Grouping templates with the same parameters together | Yes: template Foo(T, U) { class Bar { ... } T foo(T t, U u) { ... } } Foo!(int,long).Bar b; return Foo!(char,int).foo('c',3); | Sort of, using a class' members: template<class T, class U>
class Foo {
    class Bar { ... };
    static T foo(T t, U u) { ... }
};
Foo<int, long>::bar b;
return Foo<char, int>::foo('c', 3);
 | 
| Deducing this parameter type | Yes | C++23 Yes | 
| Compile time execution of functions | Yes: int factorial(int i) { if (i == 0) return 1; else return i * factorial(i - 1); } pragma(msg, factorial(6)); | C++98 No C++11 Yes, but only for constexpr functions. constexpr was highly restricted in C++11, but each subsequent standard has eased these restrictions. | 
| Parameters | D | C++ | 
| Type Parameters | Yes: class Foo(T) { T x; } Foo!(int) f; | Yes: template<class T>
class Foo
{
    T x;
};
Foo<int> f;
 | 
| Integral Parameters | Yes: void foo(int i)() { int v = i; } | Yes: template<int i>
void foo()
{
    int v = i;
}
 | 
| Pointer Parameters | Yes, a pointer to object or function | Yes, a pointer to object or function | 
| Reference Parameters | No, but an alias parameter can be used instead (see below). | Yes: template<double& D>
void foo()
{
    double y = D;
}
 | 
| Pointer to Member Parameters | No, D does not have pointers to members. It does have delegates however, which can be used as parameters | Yes | 
| Template Template Parameters | Yes: class Foo(T, alias C) { C!(T) x; } | Yes: template<class T,
         template<class U> class C>
class Foo
{
    C<T> x;
};
 | 
| Alias Parameters | Yes, any symbol can be passed to a template as an alias: void bar(int); void bar(double); void foo(T, alias S)(T t) { S(t); } // calls bar(double) foo!(double, bar)(1); | No | 
| Floating Point Parameters | Yes: class Foo(double D) { double x = D; } ... Foo!(1.6) F; | C++98 No C++11 Yes: template<float f>
void foo()
{
    int v = f;
}
 | 
| String Parameters | Yes: void foo(string format)(int i) { writefln(format, i); } ... foo!("i = %s")(3); | C++98 No C++17 Only indirectly: template <const char *str>
struct S {};
S<"Foo"> foo1; // error A string literal argument is still illegal
const char foo_str[] = "foo";
S<foo_str> foo2; // Literal types are allowed, including arrays.
 | 
| Local Class Parameters | Yes | C++98 No C++17 Yes | 
| Local Variable Parameters | Yes | No | 
| Parameter Default Values | Yes: class Foo(T = int) { T x; } | Yes: template<class T = int>
class Foo
{
    T x;
};
 | 
| Variadic Parameters | Yes, Variadic Templates: void print(A...)(A a) { foreach(t; a) writeln(t); } | C++98 No C++11 Yes: // Need a zero- or one-argument version
// to handle the final argument.
void print() {};
template <class Arg, class... Args>
void print(const Arg& arg, Args&&... args)
{
    writeln(arg);
    print(std::forward<Args>(args)...);
}
Note that the example above was
                improved somewhat in C++17 using
                Fold Expressions:template <class Args...>
void print(Args...&& args)
{
    (writeln(std::forward<Args.>(args)), ...);
}
 | 
| Specializations | D | C++ | 
| Explicit Specialization | Yes: class Foo(T : int) { T x; } | Yes: template<>
class Foo<int>
{
    int x;
};
 | 
| Partial Specialization | Yes: class Foo(T : T*, U)
{
    T x;
}
 | Yes: template<class T, class U>
class Foo<T*, U>
{
    T x;
};
 | 
| Partial specialization derived from multiple parameters | Yes: class Foo(T : Bar!(T, U), U)
{
    ...
}
 | Yes: template<class T, class U>
class Foo< Bar<T,U> >
{
    ...
};
 | 
| Can specializations exist without a primary template? | Yes | No | 
| Other | D | C++ | 
| Exported Templates | Yes, it falls out as a natural consequence of modules | C++98 Yes, but was only support in compilers based on EDG's front end. C++11 Was removed from the language due to a lack of support. | 
| SFINAE (Substitution Failure Is Not An Error) | No | Yes | 
| Parse Template Definition Bodies before Instantiation | Yes | Not required by the Standard, but some implementations do | 
| Overloading Function Templates with Functions | Yes: void foo(T)(T t) { } void foo(int i) { } | Yes: template<class T>
void foo(T t) { }
void foo(int i) { }
 | 
| Implicit Function Template Instantiation | Yes | Yes | 
| Templates can be evaluated in scope of instantiation rather than definition | Yes, Template Mixins | No, but can be faked using macros | 
| Can extract arguments of template instance | Yes: struct Bar(T1, T2) { } alias BarInst = Bar!(int, float); static if (is(BarInst : Template!Args, alias Template, Args...)) { pragma(msg, Args); // (int, float) } | No | 
| Parsing Idiosyncracies | D | C++ | 
| Context-Free Grammar | Yes: class Foo(int i) { ... } Foo!(3 > 4) f; | No: template<int i> class Foo
{
    ...
};
Foo<3 > 4> f; // error 
 | 
| Distinguish template arguments from other operators | Yes: class Foo(T) { ... } class Bar(int i) { ... } Foo!(Bar!(1)) x1; | C++98 No: template<class T> class Foo
{
    ...
};
template<int i> class Bar
{
    ...
};
Foo<Bar<1>> x1; // error 
Foo<Bar<1> > x2;
C++11 Partially fixed by Right Angle Brackets N1757 | 
| Redeclaration of Template Parameter | Yes: class Foo(T) { int T; void foo() { int T; } } | No: template<class T>
class Foo
{
    int T; // error 
    void foo()
    {
        int T; // error 
    }
};
 | 
| Dependent Base Class Lookup | Yes: class Foo(T) { alias A = int; } class Bar(T) : Foo(T) { A x; } | No: template<class T>
class Foo
{
  public:
    typedef int A;
};
template<class T>
class Bar : Foo<T>
{
  public:
    A x; // error 
};
 | 
| Forward Referencing | Yes: int g(void *); class Foo(T) { int foo() { return g(1); } } int g(int i); | No: int g(void *);
template<class T>
class Foo
{
    int foo()
    {
        return g(1); // error 
    }
};
int g(int i);
 | 
| Member templates parseable without hints | Yes: class Foo { Foo bar!(int I)(); } void abd(T)(T f) { T f1 = f.bar!(3)(); } | No: class Foo
{
  public:
    template<int> Foo *bar();
};
template<class T> void abc(T *f)
{
    T *f1 = f->bar<3>(); // error 
    T *f2 = f->template bar<3>();
}
 | 
| Dependent type members parseable without hints | Yes: class Foo(T)
{
    T.A* a1;
}
 | No: template<class T> class Foo
{
  public:
    T::A *a1; // error 
    typename T::A *a2;
};
 | 
Copyright © 1999-2025 by the D Language Foundation | Page generated by
Ddoc on Mon Mar 31 10:27:48 2025