これを独自に実装するというのは車輪の再発明になりますが,例えばテンプレートで配列の場合のみ特殊化したい場合,どう書けば良いのか,などテンプレートの書き方について色々と勉強になります.
なお,ここではC++17以降を想定しています.
integral_constantの実装
まず,integral_constantを実装します.これは,type_traitsにある色々な判定系のテンプレートの土台になっています.また,boolの場合に限定したbool_constantエイリアステンプレートや,その値がtrueの場合のtrue_type,false_typeは,型が特定の条件を満たすか判定する際の土台として使います.template <class T, T v>
struct integral_constant {
static constexpr T value = v;
using value_type = T;
using type = integral_constant<T, v>
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; }
};
template <bool B>
using bool_constant = integral_constant<bool, B>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
is_sameおよびis_same_vの実装
次に,2つのテンプレートパラメータの型が同じ場合にtrue,違う場合にfalseとなるvalueを持ったis_sameという型を実装します.また,通常はis_same<X, Y>::valueと書いて値を読み取るところを,C++14以降では,直接読み取れるようにした
is_same_vも追加されているので,そちらも実装します.
template <class T, class U>
struct is_same : false_type {};
template <class T>
struct is_same : true_type {};
template <class T, class U>
inline constexpr bool is_same_v = is_same<T, >::value;
このように,通常はfalse_typeを継承していて,型が同じ場合を特殊化してtrue_typeを継承するようにすることで,valueの値は型が同じときだけtrueになります.
これを実装しておくと,次に実装する,型を変形するtype traitsのテストがしやすくなります.is_sameも,次のようにstatic_assertを利用してテストしておきましょう.
static_assert(is_same_v<int, int>);
static_assert(!is_same_v<int, const int>);
本当は,もっと色々なパターンをテストしておいた方が良いでしょうが,その辺は実装する際に色々考えてみると良いでしょう.
remove/add系の実装
次に,型を変形するtype traitsを実装します.何故かというと,その型が何なのかを判定するis_XXX系のtype_traitsではconstなどは無視するケースも多々あり,そこでこれから実装するremove_constなどを利用するからです.
これらの型を変形するtype traitsには,typeという名前で変形後の型にアクセスできます.
まずは,constを取り除くremove_constを実装しましょう.また,typeに直接アクセスできるremove_const_tも実装します.
template <class T>
struct remove_const
{
using type = T;
};
template <class T>
struct remove_const
{
using type = T;
};
template <class T>
using remove_const_t = typename remove_const<T>::type;
エイリアステンプレートでtypeと取り出す際には,typenameが必要です.
逆に,constを付けるadd_constの実装はシンプルです.
template <class T>
struct add_const
{
using type = const T;
};
template <class T>
using add_const_t = typename add_const<T>::type;
constで無い型にはconstが付き,既にconstな型なら追加のconstは無視されるんですね.
では,テストを書いておきましょう.
static_assert(is_same_v<remove_const<int>, int>);
static_assert(is_same_v<remove_const<const int>, int>);
static_assert(is_same_v<remove_const<const int *>, const int *>);
static_assert(is_same_v<remove_const<const int * const>, const int *>);
本当は,もう少し色々書くべきですが,省略します.ここで注目すべきは,ポインタの場合,constはポインタに対して付いているものが取り除かれる,ということです.
const int *がint *になると思った方もいるかもしれませんが,そうはなりません.
こんな感じで,独自のtype_traitsを実装していきます.
remove_constとadd_constのconstをvolatileに置き換えるだけで,remove_volatileとadd_volatileが作れます.
この2つを組み合わせれば,remove_cvやadd_cvが作れます.
その2は……気が向いたら書きます.
0 件のコメント:
コメントを投稿