#include <iostream>

template<typename T>
constexpr T shift_left(const T value) { return value << 1; }

template<typename T>
constexpr T increment(const T value) { return value + 1; }

template<typename T>
constexpr T bicrement(const T value) { return value + 2; }

template<typename T>
constexpr T test(const T value) { return bicrement(shift_left(increment(value))); }

template <typename T, T(*INC)(const T), T start = {}>
struct managed_enum
{
	enum
	{
		a = start,
		b = INC(a),
		c = INC(b),
	};
};

template <typename T, T(*INC)(const T), T start = {}>
struct managed_strong_enum
{
	enum class value : T
	{
		a = start,
		b = INC(a),
		c = INC(b),
	};
};

int main()
{
	using shifted   = managed_enum<int, shift_left<int>, 1>;
	using increment = managed_enum<int, increment<int>>;
	using bicrement = managed_enum<int, bicrement<int>>;

	using test = managed_strong_enum<int, test<int>, 5>;

	std::cout << "[<< 1] -> a: " << shifted::a
					  << "\tb: " << shifted::b
					  << "\tc: " << shifted::c << '\n';

	std::cout << "[ +1 ] -> a: " << increment::a
					  << "\tb: " << increment::b
					  << "\tc: " << increment::c << '\n';

	std::cout << "[ +2 ] -> a: " << bicrement::a
					  << "\tb: " << bicrement::b
					  << "\tc: " << bicrement::c << '\n';

	std::cout << "[test] -> a: " << int(test::value::a)
					  << "\tb: " << int(test::value::b)
					  << "\tc: " << int(test::value::c) << '\n';

	return 0;
}