Non-type template parameters are obviously ones that aren't types, for example:
template<int x>
void foo() { cout << x; }
There are other options than int
in that case, and I'd like to refer to this great answer.
Now, there's one thing that bugs me: structs. Consider:
struct Triple { int x, y, z; };
Triple t { 1, 2, 3 };
template<Triple const& t>
class Foo { };
Now, using normal nontype reference semantics, we can write:
Foo<t> f;
What's worth noting here is that t
can't be constexpr
or even const
, because that implies internal linkage, which basically means that the line won't compile. We can bypass that by declaring t
as const extern
. That itself might be a bit weird, but the one that really made me wonder was why this isn't possible:
Foo<Triple { 1, 2, 3 }> f;
We get a really decent error from compiler:
error:
Triple{1, 2, 3}
is not a valid template argument for typeconst Triple&
because it is not an lvalue.
We can't specify Triple
in template by value, because that's disallowed. However, I fail to understand the real problem with that small line of code. What's the reasoning behind not allowing using structs as value parameters. If I can use three int
s, why not a struct of three ints? If it has only trivial special members, it shouldn't be really different in handling than just three variables.
Which parameter is legal for non-type template? Explanation: The following are legal for non-type template parameters:integral or enumeration type, Pointer to object or pointer to function, Reference to object or reference to function, Pointer to member.
The entities of variable, function, struct, and class can have templates, which involve declaration and definition. Creating a template also involves specialization, which is when a generic type takes an actual type. The declaration and the definition of a template must both be in one translation unit.
A non-type template argument provided within a template argument list is an expression whose value can be determined at compile time. Such arguments must be constant expressions, addresses of functions or objects with external linkage, or addresses of static class members.
Templates can be template parameters. In this case, they are called template parameters. The container adaptors std::stack, std::queue, and std::priority_queue use per default a std::deque to hold their arguments, but you can use a different container.
Updated answer for c++20 users:
C++20 adds support for class literal (class with constexpr
constructor) non-type template parameters, which would allow the example in the original question to work, provided the template parameter is accepted by value:
template<Triple t> // Note: accepts t by value
class Foo { };
// Works with unnamed instantiation of Triple.
Foo<Triple { 1, 2, 3 }> f1 {};
// Also works if provided from a constexpr variable.
constexpr Triple t { 1, 2, 3 };
Foo<t> f2 {};
Further, all template parameter instances of Triple { 1, 2, 3 }
throughout the program will refer to the same static storage duration object:
template<Triple t1, Triple t2>
void Func() {
assert(&t1 == &t2); // Passes.
}
constexpr Triple t { 1, 2, 3 };
int main()
{
Func<t, Triple {1, 2, 3}>();
}
From cppreference:
An identifier that names a non-type template parameter of class type T denotes a static storage duration object of type const T, called a template parameter object, whose value is that of the corresponding template argument after it has been converted to the type of the template parameter. All such template parameters in the program of the same type with the same value denote the same template parameter object.
Note that there are quite a few restrictions on the class literal types allowable by template parameters. For more detail, checkout this blog post I wrote explaining the usage and restrictions of literal class NTTPs: Literal Classes as Non-type Template Parameters in C++20.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With