One of my most beloved/evil inventions I've had the fortune to come across is the constexpr counter, aka stateful metaprogramming. As mentioned in the post, it seems to be legal under C++14, and I'm wondering has anything changed with C++17?
The following is an implementation largely based on the post
template <int N>
struct flag
{
friend constexpr int adl_flag(flag<N>);
constexpr operator int() { return N; }
};
template <int N>
struct write
{
friend constexpr int adl_flag(flag<N>) { return N; }
static constexpr int value = N;
};
template <int N, int = adl_flag(flag<N>{})>
constexpr int read(int, flag<N>, int R = read(0, flag<N + 1>{}))
{
return R;
}
template <int N>
constexpr int read(float, flag<N>)
{
return N;
}
template <int N = 0>
constexpr int counter(int R = write<read(0, flag<0>{}) + N>::value)
{
return R;
}
And we use it as
static_assert(counter() != counter(), "Your compiler is mad at you");
template<int = counter()>
struct S {};
static_assert(!std::is_same_v<S<>, S<>>, "This is ridiculous");
This by the way, is a direct contradiction to Storing States in C++ Metaprogramming?
read(0, flag<N + 1>{})
not result in an infinite loop? The literal 0 causes it to call the first overload (int
being preferred overfloat
), which will naturally call it again and again and again. What is the terminating condition? – Nipperint
overload ofread(0, flag<N + 1>{})
cannot be called for some large enoughN
since we have not yet definedadl_flag(flag<N + 1>)
, therefore thefloat
overload would be called. For the full explanation, the linked post is written excellently. – Phytohormone