Why can't a struct be passed as value as template non-type parameter?
It would be easy to make just this bit work, but then people would complain about how using struct template parameters does not work in all the same situations the other template parameters do (consider partial specializations, or what to do with operator==
).
In my opinion, it's too messy to get the whole cake, and getting just one tiny slice isn't satisfying enough, and possibly more frustrating. Making just this tiny bit work won't give me more power than something like the following, which has the additional advantage of working with all kinds of stuff (including partial specializations) out of the box.
template <int X, int Y, int Z>
struct meta_triple {
// static value getters
static constexpr auto x = X;
static constexpr auto y = Y;
static constexpr auto z = Z;
// implicit conversion to Triple
constexpr operator Triple() const { return { X, Y, Z }; }
// function call operator so one can force the conversion to Triple with
// meta_triple<1,2,3>()()
constexpr Triple operator()() const { return *this; }
};
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.
You can define t
as const extern
, giving it external linkage. Then the construct works:
struct Triple { int x, y, z; };
const extern Triple t { 1, 2, 3 };
template<Triple const& t>
class Foo { };
Foo<t> f;
Live example.
The reason why you can't pass a temporary to a reference template parameter is that the parameter is a reference. You'd get the same error if the template parameter was const int&
and you tried to pass 7
. Example.
EDIT
The difference between three int
s and a struct containing three int
s is that all literals of type int
are really the same value (all occurences of 7
are just seven), while each constructor call to a structure conceptually creates a new instance. Take this hypothetical example:
template <Triple t>
struct Foo {};
Foo<Triple {1, 2, 3}> f1;
Foo<Triple {1, 2, 3}> f2;
I think it would introduce extra complexity to "match" those two constructor invocations into the same template instantiation.