[c++]std::bitset と scoped enumでビットフラグ

#`enum`をビットフラグとして使いたい！
という欲求は誰にでもあるようで検索すると様々な答えがあります。

>
プレフィクスを付けて`unscoped enum`を使う
マクロを使って演算子をオーバーロードする
名前空間、クラススコープに閉じ込める
別の型として自作
...
参考：https://jumble-note.blogspot.com/2017/12/c.html

それぞれ一長一短あって自分も`SFINAE`の練習で作った`operator overload`で我慢していました。

```c++
enum class Type {
	A = 1 << 0,
	B = 1 << 1,
	C = 1 << 2,
	...  ↑ここが面倒！
};
```


##`std::bitset`という選択肢
（とくに使うわけでもないのに）`std::vector<bool>`の特殊化について調べていたある日のこと。
`std::bitset`という見慣れぬコンテナを見つけ、さらに検索をかけた結果ビットフラグだと判明、これで`scoped enum`がキャストなしで使えたら完ぺきでは？
ただラッパー作るの面倒だから他にいい方法ないものかと調べたけどとくに見つからず。

あきらめきれずにソースとリファレンスを眺めていると、ビット位置を引数にとるメンバ関数は意外と少ないのがわかったので`（set,reset,flip,test,operator[]くらい）`
これくらいなら作っちゃおうか、ということでできたのがこれです。

ついでに便利関数も追加してます。

##環境
visual stdio 2019 16.11.2 / c++ latest

#ソース
```c++:EnumBitSet.hpp
#pragma once

#include <bitset>
#include <initializer_list>
#include <type_traits>
#include <concepts>//C++20

/// <summary>
/// std::bitsetの引数にenum classを使うためのラッパー
/// </summary>
template <auto ENUM_MAX>
requires std::is_enum_v<decltype(ENUM_MAX)>//c++20
class EnumBitSet :public std::bitset<static_cast<size_t>(ENUM_MAX)> {
public:

	using Enum = decltype(ENUM_MAX);
	using Base = std::bitset<static_cast<size_t>(ENUM_MAX)>;

	static constexpr ptrdiff_t _Bits = static_cast<size_t>(ENUM_MAX);


	//継承元と同じ名前で定義してオーバーロード
	using Base::bitset;
	using Base::set;
	using Base::reset;
	using Base::flip;
	using Base::test;
	using Base::any;
	using Base::none;
	using Base::all;
	using Base::operator[];

	using _Ty = Base::_Ty;




	constexpr EnumBitSet() noexcept : Base::bitset() {}


	constexpr EnumBitSet(_Ty ty) noexcept : Base::bitset(ty) {}

	/// <summary>
	/// 全て 0/1で作成
	/// </summary>
	constexpr EnumBitSet(bool sw) noexcept : Base::bitset(sw ? get_max_ty() : 0) {}
	/// <summary>
	/// posのフラグを立てて作成
	/// </summary>
	constexpr EnumBitSet(Enum pos) noexcept : EnumBitSet({ pos }) {}

	/// <summary>
	/// posListのすべてのフラグを立てて作成
	/// </summary>
	constexpr EnumBitSet(std::initializer_list<Enum> posList) noexcept : Base::bitset(to_ty(posList)) {}




	//Enum用にラップ

	EnumBitSet& set(Enum _Pos, bool _Val = true) {
		Base::set(EToS(_Pos), _Val);
		return *this;
	}
	EnumBitSet& reset(Enum _Pos) {
		Base::reset(EToS(_Pos));
		return *this;
	}
	EnumBitSet& flip(Enum _Pos) {
		Base::flip(EToS(_Pos));
		return *this;
	}

	_NODISCARD bool test(Enum _Pos) const {
		return Base::test(EToS(_Pos));
	}

	_NODISCARD constexpr bool operator[](Enum _Pos) const {
		return Base::operator[](EToS(_Pos));
	}
	_NODISCARD Base::reference operator[](Enum _Pos) {
		return Base::operator[](EToS(_Pos));
	}



	//EnumBitSet同士 Baseにすることで別クラスで実体化したやつも同じ大きさなら演算できるようになる

	EnumBitSet& set(const Base& mask, bool _Val = true)noexcept {
		if(!_val){
			 return reset(mask);
		}
		*this |= mask;
		return *this;
	}
	EnumBitSet& reset(const Base& mask)noexcept {
		*this &= ~mask;
		return *this;
	}
	EnumBitSet& flip(const Base& mask)noexcept {
		*this ^= mask;
		return *this;
	}
	/// <summary>
	///  引数の1のビットを対象に any
	/// </summary>
	_NODISCARD bool any(const Base& mask) const noexcept {
		return (*this & mask).any();
	}

	/// <summary>
	///  引数の1のビットを対象に none
	/// </summary>
	_NODISCARD bool none(const Base& mask) const noexcept {
		return !any(mask);
	}

	/// <summary>
	///  引数の1のビットを対象に all
	/// </summary>
	_NODISCARD bool all(const Base& mask) const noexcept {
		return (*this & mask) == mask;
	}



	//initializer_list用これがないと set({ Enum::A,Enum::B }) でエラー

	inline EnumBitSet& set(std::initializer_list<Enum> posList, bool _Val = true)noexcept {
		return set(EnumBitSet(posList),_val);
	}
	inline EnumBitSet& reset(std::initializer_list<Enum> posList)noexcept {
		return reset(EnumBitSet(posList));
	}
	inline EnumBitSet& flip(std::initializer_list<Enum> posList)noexcept {
		return flip(EnumBitSet(posList));
	}
	_NODISCARD inline bool any(std::initializer_list<Enum> posList) const noexcept {
		return any(EnumBitSet(posList));
	}
	_NODISCARD inline bool none(std::initializer_list<Enum> posList) const noexcept {
		return none(EnumBitSet(posList));
	}
	_NODISCARD inline bool all(std::initializer_list<Enum> posList) const noexcept {
		return all(EnumBitSet(posList));
	}



	//変換
	_NODISCARD static constexpr size_t EToS(Enum e)  noexcept {
		return static_cast<size_t>(e);
	}

	//ビット列が全部１の場合の数を_Ty型に変換して返す
	_NODISCARD static constexpr _Ty get_max_ty() {
		_Ty ty = 0;

		for (auto n = _Bits - 1; n >= 0; n--) {
			ty += _Ty(1) << n;
		}
		return ty;
	}

	//ビット列を_Ty型に変換して返す
	_NODISCARD static constexpr _Ty to_ty(std::initializer_list<Enum> posList) {
		_Ty ty = 0;
		std::array<bool, _Bits> added{};

		for (auto&& pos : posList) {
			auto n = EToS(pos);
			if (!added[n]) {
				ty += _Ty(1) << n;
				added[n] = true;
			}
		}
		return ty;
	}
};
```

##使い方
```c++
enum class Type {
	A,
	B,
	C,
	MAX
};
using TypeSet = EnumBitSet<Type::MAX>;      //準備はこれだけ！

constexpr TypeSet AB = { Type::A,Type::B }; //複合ビットもconstexprで書ける

int main() {
	TypeSet typeSet{ Type::B,Type::C };     //BとCが1の状態で初期化

	typeSet.set(Type::A);                   //Aを1に
	typeSet.flip(AB);                       //AとBを反転

	auto b = typeSet[Type::B];              //Bにアクセス  autoは std::bitset<Type::MAX>::reference
	if (!b) {
		b = true;

		if (typeSet.any({ Type::A,Type::C })) {  //AかCが1ならtrue
			printf(typeSet.to_string().c_str()); //"110" -> C:1, B:1, A:0
		}
	}
}
```

##説明
・`std::bitset`を継承したラッパークラスです。
・どうせ`std::bitset`に渡す際`size_t`になるため、`underlying_type`は使ってません。
・`concepts`、`非型テンプレートパラメータのauto宣言`など新しめのがありますが特に難しいところはないと思います。
・`//c++20`のコメントがある行を消すとc++17環境でも動くと思いますが、`enum`以外も型引数に入るようになっちゃいます。
・演算は関数`（set,reset,flip...）`に一本化しましたが、コンストラクタで`EnumBitSet`に変換すればビット演算子も使えます。

・constexpr、コピーとムーブ、完全転送あたりいまいちわかってないのでツッコミあればお願いします。

#作り終わってから
勢いで作ったため`c++ bitset enum`で検索すれば良かったのでは・・・？
と思ったけど継承して便利なのまで作ってるのは無かったので記事にしました。

~~死ぬほど便利なのでc++enumビットフラグ論争を終わらせます！~~

#参考
bitset
https://cpprefjp.github.io/reference/bitset/bitset.html
concepts
https://cpprefjp.github.io/lang/cpp20/concepts.html
非型テンプレートパラメータのauto宣言
https://cpprefjp.github.io/lang/cpp17/declaring_non-type_template_arguments_with_auto.html
bitset+enumの実装例 メンバ変数にbitsetを持つのが多いみたいです
https://m-peko.github.io/craft-cpp/posts/different-ways-to-define-binary-flags/
https://stackoverflow.com/questions/17350214/using-enum-class-with-stdbitset