C++ Templates

【C++ Templates(17)】Traits的实现(下)

2018-06-13  本文已影响21人  downdemo

07 其他traits技术

7.1 If-Then-Else

// traits/ifthenelse.hpp

#ifndef IFTHENELSE_HPP
#define IFTHENELSE_HPP

// primary template: yield the second argument by default and rely on
// a partial specialization to yield the third argument if COND is false
template<bool COND, typename TrueType, typename FalseType>
struct IfThenElseT {
    using Type = TrueType;
};

// partial specialization: false yields third argument
template<typename TrueType, typename FalseType>
struct IfThenElseT<false, TrueType, FalseType> {
    using Type = FalseType;
};

template<bool COND, typename TrueType, typename FalseType>
using IfThenElse =
    typename IfThenElseT<COND, TrueType, FalseType>::Type;

#endif // IFTHENELSE_HPP
// traits/smallestint.hpp

#include <limits>
#include "ifthenelse.hpp"

template<auto N>
struct SmallestIntT {
    using Type =
        typename IfThenElseT<N <= std::numeric_limits<char> ::max(), char,
            typename IfThenElseT<N <= std::numeric_limits<short> ::max(), short,
                typename IfThenElseT<N <= std::numeric_limits<int> ::max(), int,
                    typename IfThenElseT<N <= std::numeric_limits<long>::max(), long,
                        typename IfThenElseT<N <= std::numeric_limits<long long>::max(),
                        long long, // then
                        void // fallback
                    >::Type
                >::Type
            >::Type
        >::Type
    >::Type;
};
// ERROR: undefined behavior if T is bool or no integral type:
template<typename T>
struct UnsignedT {
    using Type = IfThenElse<std::is_integral<T>::value
            && !std::is_same<T,bool>::value,
        typename std::make_unsigned<T>::type,
        T>;
};
typename std::make_unsigned<T>::type
// yield T when using member Type:
template<typename T>
struct IdentityT {
    using Type = T;
};

// to make unsigned after IfThenElse was evaluated:
template<typename T>
struct MakeUnsignedT {
    using Type = typename std::make_unsigned<T>::type;
};

template<typename T>
struct UnsignedT {
    using Type = typename IfThenElse<std::is_integral<T>::value
        && !std::is_same<T,bool>::value,
        MakeUnsignedT<T>,
        IdentityT<T>
        >::Type;
};
template<typename T>
struct UnsignedT {
    using Type = typename IfThenElse<std::is_integral<T>::value
        && !std::is_same<T,bool>::value,
        MakeUnsignedT<T>::Type,
        T
        >::Type;
};
template<typename T>
using Identity = typename IdentityT<T>::Type;
template<typename T>
struct UnsignedT {
    using Type
        = typename std::conditional_t<std::is_integral<T>::value
        && !std::is_same<T,bool>::value,
        MakeUnsignedT<T>,
        IdentityT<T>
        >::Type;
};

7.2 检查不抛出异常的操作

template<typename T1, typename T2>
class Pair {
    T1 first;
    T2 second;
public:
    Pair(Pair&& other)
    : first(std::forward<T1>(other.first)),
    second(std::forward<T2>(other.second)) {
    }
};
// traits/isnothrowmoveconstructible1.hpp

#include <utility> // for declval
#include <type_traits> // for bool_constant

template<typename T>
struct IsNothrowMoveConstructibleT
: std::bool_constant<noexcept(T(std::declval<T>()))>
{
};
class E {
public:
    E(E&&) = delete;
};
...
std::cout << IsNothrowMoveConstructibleT<E>::value; // 编译期错误
// traits/isnothrowmoveconstructible2.hpp

#include <utility> // for declval
#include <type_traits> // for true_type, false_type, and bool_constant<>

// primary template:
template<typename T, typename = std::void_t<>>
struct IsNothrowMoveConstructibleT : std::false_type
{
};

// partial specialization (may be SFINAE'd away):
template<typename T>
struct IsNothrowMoveConstructibleT
<T, std::void_t<decltype(T(std::declval<T>()))>>
: std::bool_constant<noexcept(T(std::declval<T>()))>
{
};

7.3 traits的便利性

template<typename T1, typename T2>
Array<
    typename RemoveCVT<
        typename RemoveReferenceT<
            typename PlusResultT<T1, T2>::Type
        >::Type
    >::Type
>
operator+ (Array<T1> const&, Array<T2> const&);

7.3.1 别名模板和traits

template<typename T>
using RemoveCV = typename RemoveCVT<T>::Type;

template<typename T>
using RemoveReference = typename RemoveReferenceT<T>::Type;

template<typename T1, typename T2>
using PlusResult = typename PlusResultT<T1, T2>::Type;
Click here to view code image
template<typename T1, typename T2>
Array<RemoveCV<RemoveReference<PlusResultT<T1, T2>>>>
operator+ (Array<T1> const&, Array<T2> const&);

7.3.2 变量模板和traits

template<typename T1, typename T2>
constexpr bool IsSame = IsSameT<T1,T2>::value;
template<typename FROM, typename TO>
constexpr bool IsConvertible = IsConvertibleT<FROM, TO>::value;
if (IsSameT<T,int>::value || IsConvertibleT<T,char>::value) ...
// 简写为
if (IsSame<T,int> || IsConvertible<T,char>) ...

08 类型分类(Type Classification)

if (IsClassT<T>::value) {
    ...
}
if constexpr (IsClass<T>) {
    ...
}
class C { // primary template for the general case
    ...
};
template<typename T>
class C<T, true> { // partial specialization for class types
    ...
};

8.1 判断基本类型

// traits/isfunda.hpp

#include <cstddef> // for nullptr_t
#include <type_traits> // for true_type, false_type, and bool_constant<>

// primary template: in general T is not a fundamental type
template<typename T>
struct IsFundaT : std::false_type {
};

// macro to specialize for fundamental types
#define MK_FUNDA_TYPE(T) \
    template<> struct IsFundaT<T> : std::true_type { \
    };

MK_FUNDA_TYPE(void)

MK_FUNDA_TYPE(bool)
MK_FUNDA_TYPE(char)
MK_FUNDA_TYPE(signed char)
MK_FUNDA_TYPE(unsigned char)
MK_FUNDA_TYPE(wchar_t)
MK_FUNDA_TYPE(char16_t)
MK_FUNDA_TYPE(char32_t)

MK_FUNDA_TYPE(signed short)
MK_FUNDA_TYPE(unsigned short)
MK_FUNDA_TYPE(signed int)
MK_FUNDA_TYPE(unsigned int)
MK_FUNDA_TYPE(signed long)
MK_FUNDA_TYPE(unsigned long)
MK_FUNDA_TYPE(signed long long)
MK_FUNDA_TYPE(unsigned long long)

MK_FUNDA_TYPE(float)
MK_FUNDA_TYPE(double)
MK_FUNDA_TYPE(long double)

MK_FUNDA_TYPE(std::nullptr_t)

#undef MK_FUNDA_TYPE
MK_FUNDA_TYPE(bool)
// 扩展为
template<> struct IsFundaT<bool> : std::true_type {
    static constexpr bool value = true;
};
// traits/isfundatest.cpp

#include "isfunda.hpp"
#include <iostream>

template<typename T>
void test (T const&)
{
    if (IsFundaT<T>::value) {
        std::cout << "T is a fundamental type" << '\n';
    }
    else {
        std::cout << "T is not a fundamental type" << '\n';
    }
}

int main()
{
    test(7); // T is a fundamental type
    test("hello"); // T is not a fundamental type
}

8.2 判断复合类型

// traits/ispointer.hpp

template<typename T>
struct IsPointerT : std::false_type { // primary template: by default not a pointer
};
template<typename T>
struct IsPointerT<T*> : std::true_type { // partial specialization for pointers
    using BaseT = T; // type pointing to
};
// traits/islvaluereference.hpp

template<typename T>
struct IsLValueReferenceT : std::false_type { // by default no lvalue reference
};
template<typename T>
struct IsLValueReferenceT<T&> : std::true_type { // unless T is lvalue references
    using BaseT = T; // type referring to
};

// traits/isrvaluereference.hpp

template<typename T>
struct IsRValueReferenceT : std::false_type { // by default no rvalue reference
};
template<typename T>
struct IsRValueReferenceT<T&&> : std::true_type { // unless T is rvalue reference
    using BaseT = T; // type referring to
};

// traits/isreference.hpp

#include "islvaluereference.hpp"
#include "isrvaluereference.hpp"
#include "ifthenelse.hpp"

template<typename T>
class IsReferenceT
: public IfThenElseT<IsLValueReferenceT<T>::value,
    IsLValueReferenceT<T>,
    IsRValueReferenceT<T>
    >::Type {
};
// traits/isarray.hpp

#include <cstddef>
template<typename T>
struct IsArrayT : std::false_type { // primary template: not an array
};

template<typename T, std::size_t N>
struct IsArrayT<T[N]> : std::true_type { // partial specialization for arrays
    using BaseT = T;
    static constexpr std::size_t size = N;
};

template<typename T>
struct IsArrayT<T[]> : std::true_type { // partial specialization for unbound arrays
    using BaseT = T;
    static constexpr std::size_t size = 0;
};
// traits/ispointertomember.hpp

template<typename T>
struct IsPointerToMemberT : std::false_type { // by default no pointer-to-member
};

template<typename T, typename C>
struct IsPointerToMemberT<T C::*> : std::true_type { // partial specialization
    using MemberT = T;
    using ClassT = C;
};

8.3 判断函数类型

// traits/isfunction.hpp

#include "../typelist/typelist.hpp"

template<typename T>
struct IsFunctionT : std::false_type { // primary template: no function
};

template<typename R, typename... Params>
struct IsFunctionT<R (Params...)> : std::true_type
{ //functions
    using Type = R;
    using ParamsT = Typelist<Params...>;
    static constexpr bool variadic = false;
};

template<typename R, typename... Params>
struct IsFunctionT<R (Params..., ...)> : std::true_type { // variadic functions
    using Type = R;
    using ParamsT = Typelist<Params...>;
    static constexpr bool variadic = true;
};
using MyFuncType = void (int&) const;
template<typename R, typename... Params>
struct IsFunctionT<R (Params...) const> : std::true_type {
    using Type = R;
    using ParamsT = Typelist<Params...>;
    static constexpr bool variadic = false;
};

template<typename R, typename... Params>
struct IsFunctionT<R (Params..., ...) volatile> : std::true_type {
    using Type = R;
    using ParamsT = Typelist<Params...>;
    static constexpr bool variadic = true;
};

template<typename R, typename... Params>
struct IsFunctionT<R (Params..., ...) const volatile> :
std::true_type {
    using Type = R;
    using ParamsT = Typelist<Params...>;
    static constexpr bool variadic = true;
};

template<typename R, typename... Params>
struct IsFunctionT<R (Params..., ...) &> : std::true_type {
    using Type = R;
    using ParamsT = Typelist<Params...>;
    static constexpr bool variadic = true;
};

template<typename R, typename... Params>
struct IsFunctionT<R (Params..., ...) const&> : std::true_type {
    using Type = R;
    using ParamsT = Typelist<Params...>;
    static constexpr bool variadic = true;
};
...

8.4 判断类类型

// traits/isclass.hpp

#include <type_traits>

template<typename T, typename = std::void_t<>>
struct IsClassT : std::false_type { // primary template: by default no class
};

template<typename T>
struct IsClassT<T, std::void_t<int T::*>> // classes can have pointer-to-member
: std::true_type {
};
auto l = []{};
static_assert<IsClassT<decltype(l)>::value, "">; // succeeds
#include <iostream>

template<typename T>
class IsClassT {
private:
    typedef char One;
    typedef struct { char a[2]; } Two;
    template<typename C> static One test(int C::*);
    template<typename C> static Two test(...);
public:
    enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
    enum { No = !Yes };
};

template<typename T>
void check()
{
    if (IsClassT<T>::Yes) std::cout << "Y" << std::endl;
    else std::cout << "N" << std::endl;
}

template<typename T>
void checkT (T)
{
    check<T>();
}

class MyClass {};
struct MyStruct {};
union MyUnion {};
void myfunc() {}
enum E{e1}e;

int main()
{
    check<int>(); // N
    check<MyClass>(); // Y
    MyStruct s;
    checkT(s); // Y
    check<MyUnion>(); // Y
    checkT(e); // N
    checkT(myfunc); // N
}

8.5 判断枚举类型

// traits/isenum.hpp

template<typename T>
struct IsEnumT {
    static constexpr bool value = !IsFundaT<T>::value &&
        !IsPointerT<T>::value &&
        !IsReferenceT<T>::value &&
        !IsArrayT<T>::value &&
        !IsPointerToMemberT<T>::value &&
        !IsFunctionT<T>::value &&
        !IsClassT<T>::value;
};

09 Policy traits

9.1 只读的参数类型

template<typename T>
class RParam {
public:
    using Type = typename IfThenElseT<sizeof(T)<=2*sizeof(void*),
        T,
        T const&>::ResultT Type;
};
template<typename T>
struct RParam<Array<T>> {
    using Type = Array<T> const&;
};
// traits/rparam.hpp

#ifndef RPARAM_HPP
#define RPARAM_HPP

#include "ifthenelse.hpp"
#include <type_traits>

template<typename T>
struct RParam {
    using Type
        = IfThenElse<(sizeof(T) <= 2*sizeof(void*)
            && std::is_trivially_copy_constructible<T>::value
            && std::is_trivially_move_constructible<T>::value),
            T,
            T const&>;
};
#endif // RPARAM_HPP
// traits/rparamcls.hpp

#include "rparam.hpp"
#include <iostream>

class MyClass1 {
public:
    MyClass1 () {
    }
    MyClass1 (MyClass1 const&) {
        std::cout << "MyClass1 copy constructor called\n";
    }
};

class MyClass2 {
public:
    MyClass2 () {
    }
    MyClass2 (MyClass2 const&) {
        std::cout << "MyClass2 copy constructor called\n";
    }
};

// pass MyClass2 objects with RParam<> by value
template<>
class RParam<MyClass2> {
public:
    using Type = MyClass2;
};
// traits/rparam1.cpp

#include "rparam.hpp"
#include "rparamcls.hpp"

// function that allows parameter passing by value or by reference
template <typename T1, typename T2>
void foo (typename RParam<T1>::Type p1,
    typename RParam<T2>::Type p2)
{
    ...
}

int main()
{
    MyClass1 mc1;
    MyClass2 mc2;
    foo<MyClass1,MyClass2>(mc1,mc2);
}
// traits/rparam2.cpp

#include "rparam.hpp"
#include "rparamcls.hpp"

// function that allows parameter passing by value or by reference
template <typename T1, typename T2>
void foo_core (typename RParam<T1>::Type p1, typename RParam<T2>::Type p2)
{
    ...
}

// wrapper to avoid explicit template parameter passing
template<typename T1, typename T2>
void foo (T1 && p1, T2 && p2)
{
    foo_core<T1,T2>(std::forward<T1>(p1),std::forward<T2>(p2));
}

int main()
{
    MyClass1 mc1;
    MyClass2 mc2;
    foo(mc1,mc2); // same as foo_core<MyClass1,MyClass2>(mc1,mc2)
}

9.2 拷贝、交换和移动(第一版内容)

// traits/csmtraits.hpp
template <typename T>
class CSMtraits : public BitOrClassCSM<T, IsClassT<T>::No > {
};
template<>
class CSMtraits<MyPODType>
: public BitOrClassCSM<MyPODType, true> {
};
// traits/csm1.hpp

#include <new>
#include <cassert>
#include <stddef.h>
#include "rparam.hpp"

// primary template
template<typename T, bool Bitwise>
class BitOrClassCSM;

// partial specialization for safe copying of objects
template<typename T>
class BitOrClassCSM<T, false> {
public:
    static void copy (typename RParam<T>::ResultT src, T* dst) {
        // copy one item onto another one
        *dst = src;
    }

    static void copy_n (T const* src, T* dst, size_t n) {
        // copy n items onto n other ones
        for (size_tk=0;k<n; ++k) {
            dst[k] = src[k];
        }
    }

    static void copy_init (typename RParam<T>::ResultT src, void* dst) {
        // copy an item onto uninitialized storage
        ::new(dst) T(src);
    }

    static void copy_init_n (T const* src, void* dst, size_t n) {
        // copy n items onto uninitialized storage
        for (size_tk=0;k<n; ++k) {
            ::new((void*)((char*)dst+k)) T(src[k]);
        }
    }

    static void swap (T* a, T* b) {
        // swap two items
        T tmp(a);
        *a = *b;
        *b = tmp;
    }

    static void swap_n (T* a, T* b, size_t n) {
        // swap n items
        for (size_tk=0;k<n; ++k) {
            T tmp(a[k]);
            a[k] = b[k];
            b[k] = tmp;
        }
    }

    static void move (T* src, T* dst) {
        // move one item onto another
        assert(src != dst);
        *dst = *src;
        src->~T();
    }

    static void move_n (T* src, T* dst, size_t n) {
        // move n items onto n other ones
        assert(src != dst);
        for (size_tk=0;k<n; ++k) {
            dst[k] = src[k];
            src[k].~T();
        }
    }

    static void move_init (T* src, void* dst) {
        // move an item onto uninitialized storage
        assert(src != dst);
        ::new(dst) T(*src);
        src->~T();
    }

    static void move_init_n (T const* src, void* dst, size_t n) {
        // move n items onto uninitialized storage
        assert(src != dst);
        for (size_tk=0;k<n; ++k) {
            ::new((void*)((char*)dst+k)) T(src[k]);
            src[k].~T();
        }
    }
};
// traits/csm2.hpp

#include <cstring>
#include <cassert>
#include <stddef.h>
#include "csm1.hpp"

// partial specialization for fast bitwise copying of objects
template <typename T>
class BitOrClassCSM<T,true> : public BitOrClassCSM<T,false> {
public:
    static void copy_n (T const* src, T* dst, size_t n) {
        // copy n items onto n other ones
        std::memcpy((void*)dst, (void*)src, n);
    }

    static void copy_init_n (T const* src, void* dst, size_t n) {
        // copy n items onto uninitialized storage
        std::memcpy(dst, (void*)src, n);
    }

    static void move_n (T* src, T* dst, size_t n) {
        // move n items onto n other ones
        assert(src != dst);
        std::memcpy((void*)dst, (void*)src, n);
    }

    static void move_init_n (T const* src, void* dst, size_t n) {
        // move n items onto uninitialized storage
        assert(src != dst);
        std::memcpy(dst, (void*)src, n);
    }
};

10 In the Standard Library

上一篇 下一篇

猜你喜欢

热点阅读