#include <iostream>
// template parameter pack needs to at the end
template < unsigned int i, char ... RHS >
struct t {
// can't copy-initialize an array from another array
constexpr static char const* s()
{ return t<i-1, ' ', char(i+'0'), RHS...>::s(); };
};
template <char ... RHS >
struct t<0, RHS...> {
// can't initialize a const array inside the class,
// need to use `constexpr`
constexpr static char arr[] = {'0', RHS..., '\0'};
constexpr static char const* s()
{ return arr; }
};
// need to define the array variable, it's ODR-used
template <char ... RHS >
constexpr char t<0, RHS...>::arr[];
int main() {
std::cout << t<5>::s(); // {'0',' ','1',' ','2',' ','3',' ','4',' ','5','\0'}
}
And here's a version with "minimal changes":
#include <iostream>
#include <array>
template < unsigned int i, char ... RHS >
struct t {
constexpr static std::array<char, sizeof...(RHS)+2*i+2> s
= t<i-1, ' ', char(i+'0'), RHS...>::s;
};
template < unsigned int i, char ... RHS >
constexpr std::array<char, sizeof...(RHS)+2*i+2> t<i, RHS...>::s;
template <char ... RHS >
struct t<0, RHS...> {
constexpr static std::array<char, sizeof...(RHS)+2> s
= {{'0', RHS..., '\0'}};
};
template <char ... RHS >
constexpr std::array<char, sizeof...(RHS)+2>
t<0, RHS...>::s;
int main() {
std::cout << t<5>::s.data();
}
Note how the array is copied into each class. The most-derived ("top-level") array is odr-used via .data()
, so definition of s
for the primary template is necessary. The definition of s
for the specialization isn't required here.
Instead of using a static data member, you could also construct the array inside a constexpr
function:
constexpr static std::array<char, sizeof...(RHS)+2> arr()
{ return {{'0', RHS..., '\0'}}; }
The drawback is that this returned array has automatic lifetime, so you can't pass its .data()
to the base classes.