天天看點

c++類型萃取

判斷兩個類型的關系

#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;

// is_same is used to judge two data types same or not during compiling
void test_is_same()
{
    cout << "test is_same:" << endl;
    cout << std::is_same<int, int>::value << endl;
    cout << std::is_same<int, unsigned int>::value << endl;
    cout << std::is_same<int, signed int>::value << endl;
}

// is_base_of is used to judge the relationship between two data types is inheritance
namespace test_is_base_of
{
    class A
    {
    };
    class B : A
    {
    };
    class C
    {
    };

    void test_is_base_of()
    {
        cout << "test is_base_of:" << endl;
        cout << std::is_base_of<A, B>::value << endl;
        cout << std::is_base_of<B, A>::value << endl;
        cout << std::is_base_of<C, B>::value << endl;
    }
};

// is_convertible is used to judge if the first parameter data type could be converted to the second parameter data type
namespace test_is_convertible
{
    class A
    {
    };
    class B : public A
    {
    };
    class C
    {
    };
    void test_is_convertible()
    {
        cout << "test is_convertible:" << endl;
        bool b2a = std::is_convertible<B *, A *>::value;
        bool a2b = std::is_convertible<A *, B *>::value;
        bool b2c = std::is_convertible<B *, C *>::value;

        cout << std::boolalpha;
        cout << b2a << endl;
        cout << a2b << endl;
        cout << b2c << endl;
    }
};

int main(void)
{
    test_is_same();
    test_is_base_of::test_is_base_of();
    test_is_convertible::test_is_convertible();
    return 0;
}      

類型轉換

#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;

/*
常用的類型轉換traits
const的添加與移除
引用的添加與移除
數組的修改和指針的修改
*/
void base_using()
{
    cout << std::boolalpha;
    // 添加const
    cout << std::is_same<const int, std::add_const<int>::type>::value << endl;
    // 移除const
    cout << std::is_same<int, std::remove_const<const int>::type>::value << endl;
    // 添加左值引用
    cout << std::is_same<int &, std::add_lvalue_reference<int>::type>::value << endl;
    // 添加右值引用
    cout << std::is_same<int &&, std::add_rvalue_reference<int>::type>::value << endl;
    // 移除左值引用
    cout << std::is_same<int, std::remove_reference<int &>::type>::value << endl;
    // 移除右值引用
    cout << std::is_same<int, std::remove_reference<int &&>::type>::value << endl;
    // 添加指針
    cout << std::is_same<int *, std::add_pointer<int>::type>::value << endl;
    // 移除數組頂層次元
    cout << std::is_same<int, std::remove_extent<int[]>::type>::value << endl;
    cout << std::is_same<int[2], std::remove_extent<int[][2]>::type>::value << endl;
    cout << std::is_same<int[2][3], std::remove_extent<int[][2][3]>::type>::value << endl;
    cout << std::is_same<int[2][3], std::remove_extent<int[4][2][3]>::type>::value << endl;
    // 移除數組所有次元
    cout << std::is_same<int, std::remove_all_extents<int[][2][3]>::type>::value << endl;
    // 取公共類型
    typedef std::common_type<unsigned char, short, int>::type NumericType;
    cout << std::is_same<int, NumericType>::value << endl;
}


int main(void)
{
    base_using();
    return 0;
}      

注意,根據模闆參數類建立對象時,要注意移除引用

建立對象是需要使用原始類型,不能使用引用類型,而模闆參數T可能是引用類型,是以需要提前移除可能存在的引用

// attention! 根據模闆參數類建立對象時,要注意移除引用
template <typename T>
typename std::remove_reference<T>::type *Create()
{
    typedef typename std::remove_reference<T>::type U;
    return new U();
}      

添加和移除引用執行個體

#include <iostream>
#include <type_traits>
#include <memory>

using std::cout;
using std::endl;

// 有時需要添加引用類型,比如從智能指針中擷取對象的引用時
template <class T>
struct Construct
{
    // step1: 去掉引用,将去掉引用的資料類型定義為U
    typedef typename std::remove_reference<T>::type U;
    // step3: 建立構造函數
    Construct() : m_ptr(new U)
    {
    }
    // step4: 擷取成員變量值,加上引用和const
    typename std::add_lvalue_reference<U>::type Get() const
    {
        return *m_ptr.get();
    }

private:
    // step2: 用定義的資料類型U
    std::unique_ptr<U> m_ptr;
};

int main()
{
    Construct<int> c;
    int a = c.Get();
    cout << a << endl;
    return 0;
}      

decay

對于有cv(const/volatile)操作符修飾的變量很難直接取到他的原始類型,是以西藥先移除引用,再移除cv運算符,方法如下

template <typename T>
typename std::remove_cv<typename std::remove_reference<T>::type>::type *Create()
{
    typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type U;
    return new U();
}      

但是這麼寫實在是太長了,是以有了decay,decay不光可以處理普通變量,還可以處理函數和數組,他的處理邏輯如下

1.移除引用

2.判斷是不是數組,如果是數組,把數組程式設計數組指針,結束

3.否則 判斷是不是函數,如果是函數,把函數變成函數指針,結束

4.如果都不是,移除cv符(普通變量)

void test_decay()
{
    cout << std::boolalpha;
    cout << std::is_same<int, std::decay<int>::type>::value << endl;
    cout << std::is_same<int, std::decay<int &>::type>::value << endl;
    cout << std::is_same<int, std::decay<int &&>::type>::value << endl;
    cout << std::is_same<int, std::decay<const int &>::type>::value << endl;
    cout << std::is_same<int *, std::decay<int[2]>::type>::value << endl;
    cout << std::is_same<int (*)(int), std::decay<int(int)>::type>::value << endl;
}      

std::decay對于函數來說是添加指針,利用這一點可以将函數變成函數指針,進而将函數指針變量儲存起來,在後面延遲執行

template <typename F>
struct SimpFunction
{
    using FnType = typename std::decay<F>::type;
    SimpFunction(F &f) : m_fn(f) {}
    void Run()
    {
        m_fn();
    }
    FnType m_fn;
};      

編譯期選擇std::conditional<bool B,class T, class F>

在std::conditional的模闆參數中,B如果為true,則conditional::type為T, 否則為F

#include <iostream>
#include <type_traits>

using std::cout;
using std::endl;

void test_conditional()
{
    using A = std::conditional<true, int, float>::type;
    using B = std::conditional<false, int, float>::type;
    cout << std::boolalpha;
    cout << std::is_same<int, A>::value << endl;
    cout << std::is_same<float, B>::value << endl;
    cout << std::is_same<long, std::conditional<std::is_integral<A>::value, long, int>::type>::value << endl;
    cout << std::is_same<int, std::conditional<std::is_integral<B>::value, long, int>::type>::value << endl;

    // 比較兩個類型,輸出較大的類型
    using max_size_t = std::conditional<(sizeof(long long) > sizeof(long double)), long long, long double>::type;
    cout << typeid(max_size_t).name() << endl;
}

int main(void)
{
    test_conditional();
    return 0;
}      

可以通過編譯期的判斷式來選擇類型,這給我們動态選擇類型提供了很大的靈活性