1. はじめに
c++98 で STL コンテナを自作するときに is_integral
を実装する必要があったので今回はそのまとめです。
2. is_integral とは
//Defined in header <type_traits>
namespace std {
template <class T>
struct is_integral;
template <class T>
inline constexpr bool is_integral_v = is_integral<T>::value; // C++17
}
Checks whether T is an integral type. Provides the member constant value which is equal to true, if T is the type bool, char, char8_t (since C++20), char16_t, char32_t, wchar_t, short, int, long, long long, or any implementation-defined extended integer types, including any signed, unsigned, and cv-qualified variants. Otherwise, value is equal to false.
つまり型T
が整数型かを調べます。
T が整数型かどうかで true_type
か false_type
になるように integral_constant
から継承します。
is_integral<T>::value
で bool値(true もしくは false) を取得することができます。
以下のような型が整数型として判定されます。
- bool
- char
- char16_t
- char32_t
- wchar_t
- signed char
- short int
- int
- long int
- long long int
- unsigned char
- unsigned short int
- unsigned int
- unsigned long int
- unsigned long long int
enum (列挙型) は c++ では整数型と 判定されません。
3. 実装
実装のためには以下のクラステンプレートを理解する必要があります。
- integral_constatnt
- is_integral_helper
- remove_cv
- is_integral
integral_constant
template <class _Tp, _Tp __v>
struct integral_constant {
static const _Tp value = __v;
typedef _Tp value_type;
typedef integral_constant<_Tp, __v> type;
// constexpr operator value_type() const noexcept { return value; } // c++11 なので未実装
const value_type operator()() const { return value; }
};
// integral_constantの特殊化 true_type
typedef integral_constant<bool, true> true_type;
// integral_constantの特殊化 false_type
typedef integral_constant<bool, false> false_type;
integral_constat
は value と value_type の定義を持ち、実体化すると value に bool値が代入されます。
それぞれの特殊化は typedef
によって true_type もしくは false_type に命名されます。
is_integral_helper
template <class _Tp>
struct is_integral_helper : public false_type {};
// int型でのクラステンプレートの完全特殊化
template <>
struct is_integral_helper<int> : public true_type {};
// char型でのクラステンプレートの完全特殊化
template <>
struct is_integral_helper<char> : public true_type {};
is_integral_helper
はデフォルトでは false_type を継承し、int型やchar型などの整数型の場合は完全特殊化を用いて true_type を継承します。
同様に他の型での完全特殊化も用意します。ここでは割愛します。
remove_cv
現時点では int型やchar型は判定できるようになりますが、const int
や volatile char
などconst修飾子やvolatile修飾子(コンパイルの最適化を抑制する修飾子)がついていても整数型が判定できなければいけません。
// default
template <class _Tp>
struct remove_cv {
typedef _Tp type;
};
// remove const
template <class _Tp>
struct remove_cv<const _Tp> {
typedef _Tp type;
};
// remove volatile
template <class _Tp>
struct remove_cv<volatile _Tp> {
typedef _Tp type;
};
// remove const volatile
template <class _Tp>
struct remove_cv<const volatile _Tp> {
typedef _Tp type;
};
remove_cv
は部分特殊化を用いることで const 、 volatile 、 const volatile を取り除いた型を type
で取得することができます。
以上で is_integral を作る材料が揃いました。
is_integral
template <class _Tp>
struct is_integral : is_integral_helper<typename remove_cv<_Tp>::type>::type {};
これが is_integral
の定義です。
is_integral<_Tp>::value
で bool値を取得できます。
ぱっと見ではわかりづらいかもしれないので、具体例を示します。
- const int の場合
template <class const int>
struct is_integral : is_integral_helper<typename remove_cv<const int>::type>::type {};
typename remove_cv<const int>::type
に注目して
typename remove_cv<const int>::type
/*
** const int の const が取り除かれ、
** typedef int type として命名されるから
*/
typename int
is_integral_helper<typename remove_cv<const int>::type>::type
に注目して
// remove_cv により
is_integral_helper<int>::type
/*
** template<>
** struct is_integral_helper<int> : public true_type {};
** より true_type の type メンバが使用できる
*/
true_type::type
/*
** true_type は integral_constant<bool, true> であるから
** 中身は
*/
struct integral_constant {
static const bool value = true;
typedef bool value_type;
typedef integral_constant<bool, true> type;
const value_type operator()() const { return value; }
};
/*
** typedef integral_constant<bool, type> type; より
*/
struct is_integral : integral_constant<bool, true> {};
/*
** is_integral は integral_constant<bool, true> のメンバを利用できるから
*/
is_integral::value // true
流れとしては以上です。
最後に
enable_if
で今回の is_integral
は用いるのですが、enable_if
の実装自体はそんなに難しそうじゃなかったので、この記事に追記してもいいかもしれません。
完全特殊化、部分特殊化の理解が深まってよかったです。
次は enable_if
のこと書きます。