君の名は・・・enum class

![https://qiita-image-store.s3.amazonaws.com/0/94177/bad14252-5d04-b289-a3c3-8de401452c91.png](https://qiita-image-store.s3.amazonaws.com/0/94177/7dec4a2a-72be-2af6-26d1-02a1dcafac6f.png)
![https://qiita-image-store.s3.amazonaws.com/0/94177/d0cf1077-71ed-51ed-b8b0-c114eb6afe90.png](https://qiita-image-store.s3.amazonaws.com/0/94177/8eb8ed3c-b11b-29e1-0311-7c2c0feb5d71.png)
![https://qiita-image-store.s3.amazonaws.com/0/94177/af75d6ef-1c6d-70f8-1d00-e77932af499d.png](https://qiita-image-store.s3.amazonaws.com/0/94177/d527307f-92fa-d4de-e5f2-06afc1eea59e.png)
![https://qiita-image-store.s3.amazonaws.com/0/94177/64174025-1492-17f2-9fa2-15087830ed08.png](https://qiita-image-store.s3.amazonaws.com/0/94177/659619e8-fea6-a9bd-db28-5bf569227a2c.png)

はい、雑コラすみません。きっと
[君の名は・・YARN！](http://qiita.com/endam/items/c77396705e2a158f1952)
なんてものを見たせいです(責任転嫁)。

まだ映画上映していますから見に行きましょう。原作小説も絶賛発売中なので**買いましょう**(宣伝は基本)。

[映画『君の名は。』公式サイト](http://www.kiminona.com/index.html)

# Re:C++のscoped enumで関数のフラグ指定をしたい

改めましてみなさま、ナマステ。この記事は
[C++のscoped enumで関数のフラグ指定をしたい](http://qiita.com/prickle/items/c4de8cc23556c6a3d93b)
への返信記事です。
同時に
[Re2: C++のscoped enumで関数のフラグ指定をしたい & 君の名は・・・enum class](http://qiita.com/akinomyoga/items/a5df34fc78efd86fa4ef)
から返信されています。

見てわかるように、「君の名は。」と「Re:ゼロから始める異世界生活」とC++の加重平均をとったような記事です。

---

なんか自分で作りたくなる人がいるようで2つほどこの記事が参照されています(が圧倒的akinomyoga氏の記事の影響力。そんなに異なるenum class同士の演算を定義したいか？)

- [自分なりのscoped enumビットフラグヘルパー](https://qiita.com/LNSEAB/items/6d92635b17b65be2ca64#_reference-7328c523e59b347df26c)
- [enum class 同士の計算を僕も実装してみた](https://qiita.com/leico/items/d137399e9b926678af44#_reference-823dcd50b29ea2b5a67d)

## 注意

この記事はSFINAEについてそこはかとなく理解していることが求められます。これを満たさない人は先に各自ググってから読み進んでください。

## そもそもフラグ定数とは

例えばWin32APIでレジストリの値を読み取るとき、

```cpp
HKEY key
const TCHAR* sub_key_root = _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders")
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER, sub_key_root, 0, KEY_READ | KEY_WOW64_64KEY, &key))
	throw std::runtime_error("error");
DWORD dwType = REG_SZ;
DWORD dwByte = 32;
if (ERROR_SUCCESS != RegQueryValueEx(key, _T("Personal"), 0, &dwType, nullptr, &dwByte) || REG_SZ != dwType)
	throw std::runtime_error("error");
std::basic_string<TCHAR> buf;
buf.resize(dwByte);
RegQueryValueEx(key, _T("Personal"), 0, nullptr, (LPBYTE)&buf[0], &dwByte);
buf.resize(std::char_traits<TCAHR>::length(buf.c_str()));
```

``KEY_READ | KEY_WOW64``みたいに指定するわけですが、これがいわゆるフラグ定数ですね。

## C++なのにマクロをつかうの？

というわけで、これまでC++ではフラグ用のクラスを作ってそこにstatic const定数を作ってきました。

```cpp:VS2013のios_baseの実装
template<class _Dummy>
	class _Iosb
	{	// define templatized bitmask/enumerated types, instantiate on demand
public:
	enum _Iostate
		{	// constants for stream states
		_Statmask = 0x17};

	static const _Iostate goodbit = (_Iostate)0x0;
	static const _Iostate eofbit = (_Iostate)0x1;
	static const _Iostate failbit = (_Iostate)0x2;
	static const _Iostate badbit = (_Iostate)0x4;
	static const _Iostate _Hardfail = (_Iostate)0x10;
};
 #define _BITMASK(Enum, Ty)	typedef int Ty
 class _CRTIMP2_PURE ios_base
	: public _Iosb<int>
	{	// base class for ios
public:
	_BITMASK(_Iostate, iostate);
}:
```

ちなみにC++11では``static constexpr``にするようになっています。
[std::ios_base::iostate - cppreference.com](http://en.cppreference.com/w/cpp/io/ios_base/iostate)

## static const式の問題

フラグの型がintとかの整数型になってしまうのでフラグ同士混ぜられる

```cpp
struct Flag1{
    enum type {};
    static constexpr type a = (type)1;
    static constexpr type b = (type)2;
};
struct Flag2{
    enum type {};
    static constexpr type a = (type)4;
    static constexpr type b = (type)8;
};

void f(int){}
void g(Flag1::type){}
int main()
{
    f(Flag1::a);//OK
    f(Flag2::b);//OKおおっと！？
    f(Flag1::a | Flag1::b);//OK
    f(Flag1::a | Flag2::b);//OKおおっと！？
    g(Flag1::a);//OK
    //g(Flag2::b);//NG:no matching function for call to 'g'
    //g(Flag1::a | Flag1::b);//NGおおっと！？:no known conversion from 'int' to 'Flag1::type' for 1st argument
    //g(Flag1::a | Flag2::b);//NG:no known conversion from 'int' to 'Flag1::type' for 1st argument
}
```

http://melpon.org/wandbox/permlink/L3IKKsKuToOsT5re

## C++11にはenum classがある

そこで``enum class``の出番です。

```cpp
enum class Flag1 : int {
    a = 1,
    b = 2
};
enum class Flag2 : int {
    a = 4,
    b = 8
};
void f(int){}
void g(Flag1){}
int main()
{
    //f(Flag1::a);//NG:no known conversion from 'Flag1' to 'int' for 1st argument
    //f(Flag2::b);//NG:no known conversion from 'Flag2' to 'int' for 1st argument
    //f(Flag1::a | Flag1::b);//NG:invalid operands to binary expression ('Flag1' and 'Flag1')
    //f(Flag1::a | Flag2::b);//NG:invalid operands to binary expression ('Flag1' and 'Flag2')
    g(Flag1::a);//OK
    //g(Flag2::b);//NG:no known conversion from 'Flag2' to 'Flag1' for 1st argument
    //g(Flag1::a | Flag1::b);//NGおおっと！？:invalid operands to binary expression ('Flag1' and 'Flag1')
    //g(Flag1::a | Flag2::b);//NG:invalid operands to binary expression ('Flag1' and 'Flag2')
}
```

http://melpon.org/wandbox/permlink/JDSzJ4So6ZH09Bpz

``enum class``は基底型に暗黙変換できませんし、同じ基底型のべつの``enum class``からの変換ももちろんできません。

しかしこれでは同じ``enum class``同士のOR演算もできません。

## operator overloadしよう

```cpp
enum class Flag1 : int {
    a = 1,
    b = 2
};
enum class Flag2 : int {
    a = 4,
    b = 8
};
constexpr Flag1 operator|(Flag1 l, Flag1 r){
    return static_cast<Flag1>(static_cast<int>(l) | static_cast<int>(r));
}
void f(int){}
void g(Flag1){}
int main()
{
    //f(Flag1::a);//NG:no known conversion from 'Flag1' to 'int' for 1st argument
    //f(Flag2::b);//NG:no known conversion from 'Flag2' to 'int' for 1st argument
    //f(Flag1::a | Flag1::b);//NG:no known conversion from 'Flag1' to 'int' for 1st argument
    //f(Flag1::a | Flag2::b);//NG:invalid operands to binary expression ('Flag1' and 'Flag2')
    g(Flag1::a);//OK
    //g(Flag2::b);//NG:no known conversion from 'Flag2' to 'Flag1' for 1st argument
    g(Flag1::a | Flag1::b);//OK
    //g(Flag1::a | Flag2::b);//NG:invalid operands to binary expression ('Flag1' and 'Flag2')
}
```

http://melpon.org/wandbox/permlink/MoXC6w73gQNgxUgu

## 機械的にoperator overloadを書くなんて[あなた、怠惰ですね～。](http://dic.nicovideo.jp/a/%E3%83%9A%E3%83%86%E3%83%AB%E3%82%AE%E3%82%A6%E3%82%B9%E3%83%BB%E3%83%AD%E3%83%9E%E3%83%8D%E3%82%B3%E3%83%B3%E3%83%86%E3%82%A3)

この方法の問題点は、各``enum class``ごとにoperator overloadしないといけない点です。
しかもOR演算だけでなくて、``& &= |=``ぐらい、場合によっては``~ ^ ^=``も使えてほしいと思うので、``7*[enum classの個数]``分のoperator overloadを書く羽目に・・・。
**絶対イヤだ**。

## そこでconcept的なoperator overloadですよ

### まずはconceptもどきをでっち上げる

これが肝です。それぞれの``enum class``が持つ性質というかインターフェースというかこの場合もっと言ってしまえば「どんなoperatorが使えるのか」を宣言するものと言う意味で私はconceptと言っています。

```cpp
#include <type_traits>
namespace enum_concept{
    template<typename T>
    struct has_bitwise_operators : std::false_type {};
    template<typename T>
    struct has_and_or_operators : has_bitwise_operators<T> {};
}
```

``has_and_or_operators ``が``has_bitwise_operators``を継承しているのは、AND/OR演算がその他Bit演算に含まれるので、``has_bitwise_operators``を満たす``enum class``は当然``has_and_or_operators``を満たしますね。

### いつものあれを書く

いつものあれ、で通じる人どのくらいいるんだろうか。
[std::enable_ifを使ってオーバーロードする時、enablerを使う？](http://qiita.com/kazatsuyu/items/203584ef4cb8b9e52462)
これです。次の項でSFINAEするのに使います

```cpp
namespace type_traits{
    template<bool con> using concept_t = typename std::enable_if<con, std::nullptr_t>::type;
    template<typename T> using underlying_type_t = typename std::underlying_type<T>::type;//C++11にはない
}
```

### SFINAEしつつoperator overloadを書く

あんま解説することもないけど一応。
まず、``enum``にしろ``enum class``にしろ、基底型というものが存在します。``enum class``は基底型に明示変換できます。基底型は整数型なので、これからorverload しようとしているoperatorはすでに持っています。基底型は``std::underlying_type``で取得できます

よって各operator overloadの実装戦略としては

1. 引数を基底型に``static_cast``する
2. 演算する
3. もとの型に``static_cast``する

と言うものになります。

ただ``static_cast<underlying_type_t<T>>``と書くのはだるい&可読性下がる&typoしやすくなると、３拍子揃ってやるべきではないので、``underlying_cast``というのをでっち上げています。[積極的に怠けていこうな！](http://qiita.com/akinomyoga/items/a5df34fc78efd86fa4ef#33-peel)。まあ``std::underlying_type``のtemplate第一引数にenumじゃない型を渡せないからSFINAEしておきたい、という話もありますが。C++が[どんな問題でももう一段階のラッパーをかますことで解決できる。ただし、ラッパーが多すぎるという問題を除いては。](https://cpplover.blogspot.jp/2016/03/c-p0174r0-p0189r0.html)という言語だとよくわかりますね。

```cpp
namespace detail{
    using namespace type_traits;
    template<typename T, concept_t<std::is_enum<T>::value> = nullptr>
    constexpr underlying_type_t<T> underlying_cast(T e) { return static_cast<underlying_type_t<T>>(e); }
}
template<typename T, type_traits::concept_t<enum_concept::has_and_or_operators<T>::value> = nullptr>
constexpr T operator&(T l, T r) {return static_cast<T>(detail::underlying_cast(l) & detail::underlying_cast(r));}
template<typename T, type_traits::concept_t<enum_concept::has_and_or_operators<T>::value> = nullptr>
T& operator&=(T& l, T r) {
    l = static_cast<T>(detail::underlying_cast(l) & detail::underlying_cast(r));
    return l;
}
template<typename T, type_traits::concept_t<enum_concept::has_and_or_operators<T>::value> = nullptr>
constexpr T operator|(T l, T r) {return static_cast<T>(detail::underlying_cast(l) | detail::underlying_cast(r));}
template<typename T, type_traits::concept_t<enum_concept::has_and_or_operators<T>::value> = nullptr>
T& operator|=(T& l, T r) {
    l = static_cast<T>(detail::underlying_cast(l) | detail::underlying_cast(r));
    return l;
}
template<typename T, type_traits::concept_t<enum_concept::has_bitwise_operators<T>::value> = nullptr>
constexpr T operator^(T l, T r) {return static_cast<T>(detail::underlying_cast(l) ^ detail::underlying_cast(r));}
template<typename T, type_traits::concept_t<enum_concept::has_bitwise_operators<T>::value> = nullptr>
T& operator^=(T& l, T r) {
    l = static_cast<T>(detail::underlying_cast(l) ^ detail::underlying_cast(r));
    return l;
}
template<typename T, type_traits::concept_t<enum_concept::has_bitwise_operators<T>::value> = nullptr>
constexpr T operator~(T op) {return static_cast<T>(~detail::underlying_cast(op));}
```

### 対象となるenum classを定義

```cpp
enum class Flag1 : int {
    a = 1,
    b = 2
};
enum class Flag2 : int {
    a = 4,
    b = 8
};
```

### conceptもどきのクラスのtemplate特殊化を書く

今回は``Flag1``は``& &= | |= ^ ^= ~``、``Flag2``は``& &= | |=``の演算ができるようにしてみます。

```cpp
namespace enum_concept{
    template<> struct has_bitwise_operators<Flag1> : std::true_type {};
    template<> struct has_and_or_operators<Flag2> : std::true_type {};
}
```

さきほど、``has_bitwise_operators``や``has_and_or_operators``はoperator overloadのSFINAEの条件に使っていました。ここで許可するtemplate特殊化を書くことでoperator overloadが有効になりますね。元記事の @akinomyoga さんのコメントに書かれたコードではunderlying_type との演算や他のenum classとの演算を有効にする手段も提供していますが、一体どうやったらそんなものが必要なのか理解できないので(それくらいキャスト書け)今回は省略しています。

### 使ってみる

```cpp
void f(Flag1){}
void g(Flag2){}
int main()
{
    f(Flag1::a | Flag1::b);
    f(Flag1::a & Flag1::b);
    f(Flag1::a ^ Flag1::b);
    f(~Flag1::a);
    g(Flag2::a | Flag2::b);
    g(Flag2::a & Flag2::b);
    //g(Flag2::a ^ Flag2::b);//invalid operands to binary expression ('Flag2' and 'Flag2')
    //g(~Flag2::a);//invalid argument type 'Flag2' to unary expression
}
```

http://melpon.org/wandbox/permlink/A6XI2lDkeXfHQrJ8

### 注意点

今回はC++11の範囲で書きましたが、代入もする演算子はC++14じゃないとconstexprにできません。なおVisual Studio 2013ではconstexprは使えませんが、上のコードからconstexprを置換して取り除けば多分動くと思います。

## 得られたもの

- フラグ指定する時の凡ミスが減らせるようになった
- enum classに希望が持てた

## 失ったもの

- この記事を読むのに費やした時間

## それにしても

**conceptがない世界はいややー！来世はconceptがある世界に産まれさせてくださーい！**

C++11のconceptとC++17に提案されていたconceptについては
[帰ってきたコンセプト | Boost勉強会 #16 大阪](http://ezoeryou.github.io/boost-benkyokai-oosaka-16/#/)
を見てください。まあC++17にconceptが入らないことが確定したけどな！

## 余談

凄まじくどうでもいい話ですが、冒頭の雑コラ、最初3枚はシーン的には映画最後のシーンで(三葉と瀧がタイムスリップしてる絵だけど)、2021年12月という設定らしいのでC++17とC++20は出てますね。C++23に向けて標準化委員会にまたconceptとかが提案されていることでしょう。
4枚目は2013年9月ごろのはずなので、C++11にconceptが入らないことになり間もなくC++14がでるけどやっぱりなんでconceptないんや！という時期ですね。C++17に提案されていてまたもrejectを喰らったConcept Liteの作業が始まったのが[2014/2/17らしいので](https://github.com/cplusplus/concepts-ts/commit/e0b420cefa63bc0fdf03dbe01dd189ee9c4ab64f)まだ三葉は再提案の話は知らないはずですね。

cf.)
[【ネタバレ解説】｢君の名は。｣読者と共に読解入れ替わり時系列、図解で解説!｢転校生｣どころじゃなかった…チェ・ブンブンのティーマ - Part 2](http://france-chebunbun.com/2016/08/29/post-7566/2/)