In his answer to my question
Avoiding struct in variadic template function
iavr remarked that "std::array::operator[] is constexpr only in C++14". My
question here is to make sure that GCC behavior is inconsistent and that's not
me wrongly understanding the standard.
I'm exploring different way of using some template meta programming to
initialize a two dimensional array to the Pascal triangle (with 0 outside).
In the one I'm trying here, I want to avoid as much as possible to use
template structure and in particular variadic in favor if constexpr function
and arrays.
Note for reader in a hurry: I'm putting the three following pieces of code for sake of
completeness but you dont need to understand them.
I'm using the two following pretty standard definitions:
template <typename... Ts> struct Sequence {};
template<unsigned N, unsigned... Is> struct Range {
typedef typename Range<N-1, N-1, Is...>::type type;
};
template<unsigned... Is> struct Range<0, Is...> {
typedef Sequence<std::integral_constant<unsigned int, Is>...> type;
};
Then I have the following template constexpr function which given a line of
the triangle, compute the next one:
// nextline
template <typename... SeqTis, typename T, size_t Size>
constexpr std::array<T, Size>
nextline(Sequence<SeqTis...>, const typename std::array<T, Size> ar) {
return { 1, (ar[SeqTis::value]+ar[SeqTis::value+1])... };
}
template <typename T, size_t Size>
constexpr std::array<T, Size>
nextline(const typename std::array<T, Size> ar) {
return nextline(typename Range<Size-1>::type(), ar);
}
And the following add an element at the end of a partially initialized array:
template <typename... SeqTis, typename T, size_t Size>
constexpr std::array<T, Size>
appendarray(Sequence<SeqTis...>, const typename std::array<T, Size> ar, const T el) {
return { ar[SeqTis::value]..., el };
}
template <size_t Pos, typename T, size_t Size>
constexpr std::array<T, Size>
appendarray(const typename std::array<T, Size> ar, const T el) {
return appendarray(typename Range<Pos>::type(), ar, el);
}
In these code I'm using array indexing and it perfectly works. As you can try
with:
constexpr auto ar0 = std::array<int, 3> { 1,0,0 };
constexpr auto ar1 = nextline(ar0);
constexpr auto ar2 = appendarray<2>(ar1, 12);
for (auto i: ar2) std::cout << i << " "; // prints 1 1 12
But when I try to compile the following recursive struct:
template <typename T, size_t N>
using Ar2 = std::array<std::array<T, N+1>, N+1>;
template<typename T, size_t N, size_t l> struct Loop {
constexpr static Ar2<T, N> next() {
return appendarray<l>(Loop<T, N, l-1>::next(),
nextline(Loop<T, N, l-1>::next()[l-1]));
}
};
template<typename T, size_t N> struct Loop<T, N, 0> {
constexpr static Ar2<T, N> next() {
return Ar2<T, N>({ {1, 0} });
}
};
};
Then GCC complains with
[...]
binom2.cpp:48:30: note: ‘static constexpr Ar2<T, N> Loop<T, N, l>::next() [with T = long long int; long unsigned int N = 10ul; long unsigned int l = 10ul; Ar2<T, N> = std::array<std::array<long long int, 11ul>, 11ul>]’ is not usable as a constexpr function because:
constexpr static Ar2<T, N> next() {
^
binom2.cpp:50:38: error: call to non-constexpr function ‘std::array<_Tp, _Nm>::value_type& std::array<_Tp, _Nm>::operator[](std::array<_Tp, _Nm>::size_type) [with _Tp = std::array<long long int, 11ul>; long unsigned int _Nm = 11ul; std::array<_Tp, _Nm>::reference = std::array<long long int, 11ul>&; std::array<_Tp, _Nm>::value_type = std::array<long long int, 11ul>; std::array<_Tp, _Nm>::size_type = long unsigned int]’
nextline(Loop<T, N, l-1>::next()[l-1]));
It seems that sometimes GCC allows constexpr array indexing, and sometimes it
doesn't. Am I missing something.