元ネタでは、負の表現に1の補数を使う処理系に配慮した上で、負の年にも対応しようとしていますが、一応、高速判定が売りですので、ここでは無駄はバッサリ切り捨てて、1年~のみ対応としました。また、ユリウス暦とグレゴリオ暦を動的に切り替えることもまずないので、多重定義を用いて高速化を図っています。
除算については、コンパイラや標準ライブラリの実装に依存する部分もありますが、div関数は必ずしも速くありませんので直接計算しています。一応、最適化を期待して、除算と剰余算は、両方100を右オペランドに指定してみました。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
enum gregorian_t { gregorian = 0 }; enum julian_t { julian = 1 }; inline bool is_leapyear(int year, gregorian_t = gregorian ) { if (year < 1) return false; unsigned int y = year; return (y & 3) == 0 && (y % 100 != 0 || (y / 100 & 3) == 0); } inline bool is_leapyear(int year, julian_t ) { if (year < 1) return false; unsigned int y = year; return (y & 3) == 0; } |
これだけでもよいのですが、一応、「C++関数・テンプレート集」ですので、テンプレートを使ったメタ関数も採り上げておきます。ただし、C++14以降であれば、上記の関数をそのままconstexprにした方がよいかもしれません。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
template<int Year, int Calender = gregorian> struct is_Leapyear; template<int Year> struct is_Leapyear<Year, gregorian> { static const bool value = Year < 0 ? false : (Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)); }; template<int Year> struct is_Leapyear<Year, julian> { static const bool value = Year < 0 ? false : Year % 4 == 0; }; |
このようにしておくことで、通常の関数にせよ、メタ関数にせよ、ユリウス暦とグレゴリオ暦以外にも簡単に対応できるようになります。例えば、グレゴリオ暦にも誤差があり、3200で割り切れる年は閏年ではなく、80000で割り切れる年は閏年とするような暦法を使う場合でも、適切に多重定義、またはテンプレートの特殊化を行えばよいことになります。