【C++11学習ノート】タイプ判定のtype_traits学習
14854 ワード
一、簡単なtype_traits
私の理解するtype_traitsは、C++テンプレートプロパティとstatic、enumプロパティを使用してコンパイラ定数を定義します.たとえば、
ここではstatic定数をコンパイラ定数とする特を用いてvalueを定義する.使用方法:std::integral_からconstantは派生し、static const定数やenumタイプを自分で定義する必要はありません.たとえば、
stdには2つの定義されたstdがあります::integral_constantインスタンスは、コンパイル期間のtrueタイプとfalseタイプをそれぞれ定義し、用途が広い:
二、よくあるタイプの判断type_traitsソース学習
1.is_void
宣言:
機能:
Tがvoidタイプかどうか
ソース:
説明:まずテンプレートのマッチング実装を用いて、2つのタイプが一致するか否かを判断するis_name,さらにTをc(const),v(volatile)制限子を除去してvoidタイプと一致するか否かを判断する.以下の簡単なコードでは説明しない.
2.is_floating_point
宣言
さぎょう
Tが浮動小数点タイプかどうか
ソースコード
3.is_array
宣言
さぎょう
Tが配列タイプかどうか
ソースコード
4.is_pointer
宣言
さぎょう
Tがポインタタイプ(関数ポインタを含むがメンバー(関数)ポインタを含まない)のソースコードであるかどうか
5.is_member_pointer
宣言
さぎょう
Tがメンバー関数ポインタであるか、メンバー変数ポインタタイプを指すソースコードであるか
なぜis_member_pointer_helperこれがメンバー関数ポインタ、指向メンバー変数ポインタタイプですか?
クラスメンバー関数ポインタまたはクラスメンバー変数ポインタ(A:*)をC++でどのように宣言するかを参照してください.このパラメータT U:*はどのように理解して、実はT*--Tタイプのポインタと理解して、しかしクラスUの中の、つまりクラスUのメンバー関数のポインタあるいはメンバーの変数のポインタで、以下のテストコードを見ます:
出力は
なお、クラスTにおいて、本当にintの戻り値を持つ関数があるか否か、あるいはint型変数があるか否かを判定するのではなく、Tという書き方がメンバー関数ポインタであるか否かを判定し、メンバー変数ポインタタイプを指すだけである.
6.is_class
宣言:
さぎょう
Tがクラスタイプでありunionタイプソースコードではないか
説明すると、2つのテンプレート関数が定義され、1つのパラメータはint T:*(int型クラスメンバー変数へのポインタ)、戻り値はchar(サイズは1)であり、もう1つのパラメータはすべてのタイプであり、戻り値はstruct two(サイズは2)である.
is_classはstd::integral_を継承constant< T,T v>(内部ではstatic const Tタイプ変数valueが定義され、値はv)であり、valueのタイプはboolであり、detail::test(0)のサイズが1の場合(Tがclassタイプであれば、最初のテンプレート関数testに合致する場合、その戻り値サイズは1であり、そうでなければ戻り値サイズは2)であり、unionタイプでない場合(加えて、unionタイプはstructタイプに似ており、T:*もサポートされているため)、class(またはstruct)タイプである.
7.is_base_of
宣言
さぎょう
ベースがDerivedのベースクラスかどうか
ソースコード
コードの中で最も「すごい」ところはhelper関数のマッチングです. BaseがDerivedのベースクラスでない場合、Conv()が暗黙的に変換されると、2つの候補タイプBaseとDerivedは平等であり、2つのhelper関数は一致するが、ここではルールに従って非テンプレートの関数を優先的にマッチングする.そこで私たちが望んでいた結果を得ました. BaseがDerivedのベースクラスである場合、この場合は複雑です.
この場合、Convオブジェクトがconstでない限り、operator()Base constの後ろにあるconstのため、暗黙的な変換はoperator()Derivedを呼び出すだけです.このような場合、Conv()は常に暗黙的にDerivedオブジェクトに変換されます(もちろん、上のはコードを学ぶだけで、本当に実用的であれば、多くの仕事をしなければなりません.例えば、ここではまずDerivedタイプ自体がconstではないことを確保しなければなりません.このとき、2つのhelperの最初のパラメータに対して、1つは正確に一致し、1つはベースクラスに変換され、最初はこのような状況で最初に一致すると思っていました.はい、これも必要な正しい結果なので、結果はもちろん間違いありませんが、私の考えはあまりにも無邪気です.私は2番目のパラメータの貢献を完全に抹殺したので、もしそれがなければ、最初のhelper関数は具現化されません.ましてやそれをマッチングさせることは言うまでもありません.
【参考:1.『C++11コードの最適化とエンジニアリングレベルの応用を深く応用する』2.http://en.cppreference.com/w/ 3.TR 1中is_base_ofの実現】
私の理解するtype_traitsは、C++テンプレートプロパティとstatic、enumプロパティを使用してコンパイラ定数を定義します.たとえば、
//std::integral_constant
typelate<class T, T v>
struct integral_constant
{
static const T value = v;
typedef T value_type;
typedef integral_constant type;
operator value_type() {return value;}
};
ここではstatic定数をコンパイラ定数とする特を用いてvalueを定義する.使用方法:std::integral_からconstantは派生し、static const定数やenumタイプを自分で定義する必要はありません.たとえば、
template<typename T>
struct GetSize : std::integral_constant<int, 1>
{
};
stdには2つの定義されたstdがあります::integral_constantインスタンスは、コンパイル期間のtrueタイプとfalseタイプをそれぞれ定義し、用途が広い:
typedef integral_constanttrue> true_type;
typedef integral_constantfalse> false_type;
二、よくあるタイプの判断type_traitsソース学習
1.is_void
宣言:
template<class T>
struct is_void;
機能:
Tがvoidタイプかどうか
ソース:
template<class T, class U>
struct is_same : std::false_type
{};
template<class T>
struct is_same : std::true_type
{};
template<class T>
struct is_void : std::is_same, typename std::remove_cv ::type>
{};
説明:まずテンプレートのマッチング実装を用いて、2つのタイプが一致するか否かを判断するis_name,さらにTをc(const),v(volatile)制限子を除去してvoidタイプと一致するか否かを判断する.以下の簡単なコードでは説明しない.
2.is_floating_point
宣言
template< class T >
struct is_floating_point;
さぎょう
Tが浮動小数点タイプかどうか
ソースコード
template< class T >
struct is_floating_point : std::integral_constant,std::is_same, typename std::remove_cv ::type>::value || std::is_same, typename std::remove_cv ::type>::value || std::is_same double, typename std::remove_cv ::type>::value>
{};
3.is_array
宣言
template<class T>
struct is_array;
さぎょう
Tが配列タイプかどうか
ソースコード
template<class T>
struct is_array : std::false_type {};
template<class T>
struct is_array : std::true_type {};
template<class T, std::size_t N>
struct is_array : std::true_type {};
4.is_pointer
宣言
template< class T >
struct is_pointer;
さぎょう
Tがポインタタイプ(関数ポインタを含むがメンバー(関数)ポインタを含まない)のソースコードであるかどうか
template< class T > struct is_pointer_helper : std::false_type {};
template< class T > struct is_pointer_helper : std::true_type {};
template< class T > struct is_pointer : is_pointer_helper<typename std::remove_cv::type> {};
5.is_member_pointer
宣言
template< class T >
struct is_member_pointer
さぎょう
Tがメンバー関数ポインタであるか、メンバー変数ポインタタイプを指すソースコードであるか
template< class T >
struct is_member_pointer_helper : std::false_type {};
template< class T, class U >
struct is_member_pointer_helper<T U::*> : std::true_type {};
template< class T >
struct is_member_pointer : is_member_pointer_helperstd::remove_cv ::type>
{};
なぜis_member_pointer_helper
クラスメンバー関数ポインタまたはクラスメンバー変数ポインタ(A:*)をC++でどのように宣言するかを参照してください.このパラメータT U:*はどのように理解して、実はT*--Tタイプのポインタと理解して、しかしクラスUの中の、つまりクラスUのメンバー関数のポインタあるいはメンバーの変数のポインタで、以下のテストコードを見ます:
#include
#include
int main() {
class cls {};
std::cout << (std::is_member_pointer(cls::*)>::value
? "T is member pointer"
: "T is not a member pointer") << '
';
std::cout << (std::is_member_pointer cls::*>::value
? "T is member pointer"
: "T is not a member pointer") << '
';
}
出力は
T is member pointer
T is member pointer
なお、クラスTにおいて、本当にintの戻り値を持つ関数があるか否か、あるいはint型変数があるか否かを判定するのではなく、Tという書き方がメンバー関数ポインタであるか否かを判定し、メンバー変数ポインタタイプを指すだけである.
6.is_class
宣言:
template <class T>
struct is_class;
さぎょう
Tがクラスタイプでありunionタイプソースコードではないか
namespace detail {
template <class T> char test(int T::*);
struct two { char c[2]; };
template <class T> two test(...);
}
template <class T>
struct is_class : std::integral_constant, sizeof(detail::test (0))==1 && !std::is_union ::value>
{};
説明すると、2つのテンプレート関数が定義され、1つのパラメータはint T:*(int型クラスメンバー変数へのポインタ)、戻り値はchar(サイズは1)であり、もう1つのパラメータはすべてのタイプであり、戻り値はstruct two(サイズは2)である.
is_classはstd::integral_を継承constant< T,T v>(内部ではstatic const Tタイプ変数valueが定義され、値はv)であり、valueのタイプはboolであり、detail::test(0)のサイズが1の場合(Tがclassタイプであれば、最初のテンプレート関数testに合致する場合、その戻り値サイズは1であり、そうでなければ戻り値サイズは2)であり、unionタイプでない場合(加えて、unionタイプはstructタイプに似ており、T:*もサポートされているため)、class(またはstruct)タイプである.
7.is_base_of
宣言
template <typename Base, typename Derived>
class is_base_of;
さぎょう
ベースがDerivedのベースクラスかどうか
ソースコード
template <typename Base, typename Derived,
bool = (is_class ::value && is_class::value)>
class is_base_of
{
template <typename T>
static char helper(Derived, T);
static int helper(Base, int);
struct Conv
{
operator Derived();
operator Base() const;
};
public:
static const bool value = sizeof(helper(Conv(), 0)) == 1;
};
template <typename Base, typename Derived>
class is_base_of false>
{
public:
static const bool value = is_same ::value;
};
class B
{
};
class D : public B
{
};
int main()
{
cout << boolalpha << is_base_of::value << endl;
}
コードの中で最も「すごい」ところはhelper関数のマッチングです.
この場合、Convオブジェクトがconstでない限り、operator()Base constの後ろにあるconstのため、暗黙的な変換はoperator()Derivedを呼び出すだけです.このような場合、Conv()は常に暗黙的にDerivedオブジェクトに変換されます(もちろん、上のはコードを学ぶだけで、本当に実用的であれば、多くの仕事をしなければなりません.例えば、ここではまずDerivedタイプ自体がconstではないことを確保しなければなりません.このとき、2つのhelperの最初のパラメータに対して、1つは正確に一致し、1つはベースクラスに変換され、最初はこのような状況で最初に一致すると思っていました.はい、これも必要な正しい結果なので、結果はもちろん間違いありませんが、私の考えはあまりにも無邪気です.私は2番目のパラメータの貢献を完全に抹殺したので、もしそれがなければ、最初のhelper関数は具現化されません.ましてやそれをマッチングさせることは言うまでもありません.
【参考:1.『C++11コードの最適化とエンジニアリングレベルの応用を深く応用する』2.http://en.cppreference.com/w/ 3.TR 1中is_base_ofの実現】