scoped enum用オーバーロード関数

#はじめに
今回開発時にログ用のオーバーロード関数を作成していました．しかし，scopedenumで定義した型用の関数を全種分定義するわけにもいかず，どうにかならいなかと思って調べた結果の備忘録です．

#サンプルコード
```C++:main.cpp

#include <stdio.h>
#include <vector>

// retrun macro
#define RET_MACRO(retData){\
    print(__func__, retData);\
    return retData;\
}
#define RET_VOICE_MACRO(){\
    print(__func__);\
    return;\
}

enum class Hoge
{
	A = 0,
	B,
	C,
};

// scoped enum
template<typename T>
void print(const char* funcName
, T num
, typename std::enable_if<std::is_enum<T>::value, std::nullptr_t>::type = nullptr)
{
	printf("%-20s : %d\n", funcName, num);
}
// double
void print(const char* funcName, double num)
{
	printf("%-20s : %f\n", funcName, num);
}
// int
void print(const char* funcName, int num)
{
	printf("%-20s : %d\n", funcName, num);
}
// vector
template<typename T>
struct is_vector : std::false_type {};
template<typename T>
struct is_vector<std::vector<T>> : std::true_type {};
template<typename T>
void print(const char* funcName
, T num
, typename std::enable_if<is_vector<T>::value, std::nullptr_t>::type = nullptr)
{
	printf("%-20s : vec\n", funcName);
}
// other
void print(const char* funcName, ...)
{
	printf("%-20s : --\n", funcName);
}


// return int
int retIntFuc()
{
	RET_MACRO(5);
}
// return double
double retDoubleFuc()
{
	RET_MACRO(0.5);
}

// return scoped enum
Hoge retScopedEnumFuc()
{
	RET_MACRO(Hoge::B);
}

// return vector
std::vector<int> retVectorFunc()
{
	std::vector<int> vec;
	RET_MACRO(vec);
}

// retrun other
struct Othr
{
	int a;
	int b;
};
Othr retOtherFunc()
{
	Othr other = {};
	RET_MACRO(other);
}

// retrun other
void retVoidFunc()
{
	RET_VOICE_MACRO();
}

int main()
{
	retIntFuc();
	retDoubleFuc();
	retScopedEnumFuc();
	retVectorFunc();
	retOtherFunc();
	retVoidFunc();

	return 0;
}
// return double
double retDoubleFuc()
{
    RET_MACRO(0.5);
}

// return scoped enum
Hoge retScopedEnumFuc()
{
    RET_MACRO(Hoge::B);
}

// return vector
std::vector<int> retVectorFunc()
{
    std::vector<int> vec;
    RET_MACRO(vec);
}

// retrun other
struct Othr
{
    int a;
    int b;
};
Othr retOtherFunc()
{
    Othr other = {};
    RET_MACRO(other);
}

// retrun other
void retVoidFunc()
{
    RET_VOICE_MACRO();
}

int main()
{
    retIntFuc();
    retDoubleFuc();
    retScopedEnumFuc();
    retVectorFunc();
    retOtherFunc();
    retVoidFunc();

    return 0;
}

```
#実行結果
```
retIntFuc            : 5
retDoubleFuc         : 0.500000
retScopedEnumFuc     : 1
retVectorFunc        : vec
retOtherFunc         : --
retVoidFunc          : --
```

#解説
23～51行目までのprint関数がログ用の関数で，今回の肝となるのが22～26行目のscopedenum用のprint関数です．
大まかな流れは以下通りです．
・渡された引数の型がenumか否かを「std::is_enum」にて判定を行います．enumであればtrueを返す．
・「std::enable_if」は「is_enum」がtrueとなればenbale_ifは「std::nullptr_t」型として実態化．
・これによりenum以外の場合SFINAEによって，オーバーロード解決の候補から外され，enumのときに実行される関数ができる．

私自身少しふわっとした理解なので，間違い等あれば指摘していただけるとありがたいです．
また，vectorは後で必要になったので，参考文献4を参考にメタ関数等作成しました．


#参考文献
1．[enable_if - cpprefjp - C++日本語リファレンス](https://cpprefjp.github.io/reference/type_traits/enable_if.html)
2．[enable_if クラス](https://msdn.microsoft.com/ja-jp/library/ee361639.aspx)
3．[【C++ Advent Calendar 2016 22日目】C++ で enable_if を使うコードのベストプラクティス](http://secret-garden.hatenablog.com/entry/2016/12/22/032008)
4．[C++メタ関数のまとめ](https://qiita.com/_EnumHack/items/ee2141ad47915c55d9cb)