A C++ trick is known as “stateful metaprogramming”, in which one manipulates
friend functions and template instantiation to introduce a utilizable state into
the compilation.
1
2
3
4
5
6
| constexpr bool func() { /* something */ }
constexpr bool a = func();
constexpr bool b = func();
// Think this assertion always fails? With stateful metaprogramming, think again.
static_assert(a != b);
|
Maybe ill-formed? or UB?
1
2
3
4
5
6
7
8
9
10
11
12
| auto flag(auto);
template<bool val>
struct flag_setter {
friend auto flag(auto) {}
};
int main() {
flag(1); // 1, error
flag_setter<true>{};
flag(1); // 2, ok
}
|
During the template class flag_setter
instantiation, a template class
specialization has been added to the global scope. The specialization defines
the function flag()
(by friend function). So the second invocation success.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| auto flag(auto);
template<bool val>
struct flag_setter {
friend auto flag(auto) {
}
bool value = false;
};
template<auto arg = 0, auto condition = requires { flag(arg); }>
consteval auto value() {
if constexpr (!condition) {
return flag_setter<condition> {}.value;
}
return condition;
}
int main() {
constexpr auto a = value(); // 1: false
constexpr auto b = value(); // 2: true
static_assert(a != b);
}
|
- At
// 1
the template class flag_setter
has not been instantiated. We got
false
. - After the first invocation, the template class
flag_setter
has been
instantiated. The function flag()
is defined. We got true
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| template<std::size_t N>
struct reader
{
friend auto counted_flag(reader<N>);
};
template<std::size_t N>
struct setter
{
friend auto counted_flag(reader<N>) {}
std::size_t value = N;
};
template<auto N = 0, auto tag = []{}, bool condition = requires(reader<N> red){ counted_flag(red); }>
constexpr auto next()
{
if constexpr (!condition)
{
constexpr setter<N> s;
return s.value;
}
else
{
return next<N + 1>();
}
}
int main()
{
constexpr auto a = next();
constexpr auto b = next();
constexpr auto c = next();
static_assert(a == 0 && b == 1 && c == 2);
}
|
- Is stateful metaprogramming ill-formed (yet)?
- Revisiting Stateful Metaprogramming in C++20
- Compiler Explorer