【C++11学習ノート】タイプ判定のtype_traits学習

14854 ワード

一、簡単なtype_traits
私の理解する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_offalse>
{
public:
    static const bool value = is_same::value;
};


class B
{
};

class D : public B
{
};

int main()
{
    cout << boolalpha << is_base_of::value << endl;
}

コードの中で最も「すごい」ところは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の実現】