C++ Templates

【C++ Templates(16)】Traits的实现(上)

2018-06-05  本文已影响96人  downdemo

01 一个实例:累加一个序列

1.1 Fixed Traits

#include <iostream>

template <typename T>
T accum (const T* beg, const T* end)
{
    T total{};
    while (beg != end) {
        total += *beg;
        ++beg;
    }
    return total;
}

int main()
{
    char name[] = "templates";
    int length = sizeof(name)-1;
    std::cout << accum(name, name+length) / length << '\n'; // -5
}
accum<int>(name, name+length)
template<typename T>
struct Accumulationtraits;

template<>
struct Accumulationtraits<char> {
    using AccT = int;
};

template<>
struct Accumulationtraits<short> {
    using AccT = int;
};

template<>
struct Accumulationtraits<int> {
    using AccT = long;
};

template<>
struct Accumulationtraits<unsigned int> {
    using AccT = unsigned long;
};

template<>
struct Accumulationtraits<float> {
public:
    using AccT = double;
};

template<typename T>
auto accum (const T* beg, const T* end)
{
    using AccT = typename Accumulationtraits<T>::AccT;
    AccT total{};
    while (beg != end) {
        total += *beg;
        ++beg;
    }
    return total;
}

1.2 Value Traits

template<typename T>
struct Accumulationtraits;

template<>
struct Accumulationtraits<char> {
    using AccT = int;
    static const AccT zero = 0;
};

template<>
struct Accumulationtraits<short> {
    using AccT = int;
    static const AccT zero = 0;
};

template<>
struct Accumulationtraits<int> {
    using AccT = long;
    static const AccT zero = 0;
};

template<typename T>
auto accum (const T* beg, const T* end)
{
    using AccT = typename Accumulationtraits<T>::AccT;
    AccT total = Accumulationtraits<T>::zero;
    while (beg != end) {
        total += *beg;
        ++beg;
    }
    return total;
}
template<>
struct Accumulationtraits<float> {
    using Acct = float;
    static constexpr float zero = 0.0f; // ERROR: not an integral type
};
class BigInt {
  BigInt(long long);
  ...
};

template<>
struct Accumulationtraits<BigInt> {
    using AccT = BigInt;
    static constexpr BigInt zero = BigInt{0};  // ERROR: not a literal type
};
template<>
struct Accumulationtraits<BigInt> {
    using AccT = BigInt;
    static const BigInt zero;  // 仅声明
};
// 在源文件中初始化
const BigInt Accumulationtraits<BigInt>::zero = BigInt{0};
template<>
struct Accumulationtraits<BigInt> {
    using AccT = BigInt;
    inline static const BigInt zero = BigInt{0};  // OK since C++17
};
template<typename T>
class Accumulationtraits;

template<>
struct Accumulationtraits<char> {
    using AccT = int;
    static constexpr AccT zero() {
        return 0;
    }
};

template<>
struct Accumulationtraits<short> {
    using AccT = int;
    static constexpr AccT zero() {
        return 0;
    }
};

template<>
struct Accumulationtraits<int> {
    using AccT = long;
    static constexpr AccT zero() {
        return 0;
    }
};

template<>
struct Accumulationtraits<unsigned int> {
    using AccT = unsigned long;
    static constexpr AccT zero() {
        return 0;
    }
};

template<>
struct Accumulationtraits<float> {
    using AccT = double;
    static constexpr AccT zero() {
        return 0;
    }
};
template<>
struct Accumulationtraits<BigInt> {
    using AccT = BigInt;
    static BigInt zero() {
        return BigInt{0};
    }
};
AccT total = Accumulationtraits<T>::zero();

1.3 Parameterized Traits

template<typename T, typename AT = Accumulationtraits<T>>
auto accum (const T* beg, const T* end)
{
    typename AT::AccT total = AT::zero();
    while (beg != end) {
        total += *beg;
        ++beg;
    }
    return total;
};

02 Traits versus Policies and Policy Classes

class MultPolicy {
public:
    template<typename T1, typename T2>
    static void accumulate (T1& total, const T2& value) {
        total *= value;
    }
};

template <typename T,
    typename Policy = MultPolicy,
    typename traits = Accumulationtraits<T>>
auto accum (const T* beg, const T* end)
{
    using AccT = typename traits::AccT;
    AccT total = traits::zero();
    while (beg != end) {
        Policy::accumulate(total, *beg);
        ++beg;
    }
    return total;
}

int main()
{
    int num[] = { 1, 2, 3, 4, 5 };
    std::cout << accum<int,MultPolicy>(num, num+5);
}

2.1 Member Templates versus Template Template Parameters

template <typename T1, typename T2>
class MultPolicy {
public:
    static void accumulate (T1& total, const T2& value) {
        total *= value;
    }
};

template<typename T,
    template<typename, typename> class Policy = MultPolicy,
    typename traits = Accumulationtraits<T>>
auto accum (const T* beg, const T* end)
{
    using AccT = typename traits::AccT;
    AccT total = traits::zero();
    while (beg != end) {
        Policy<AccT, T>::accumulate(total, *beg);
        ++beg;
    }
    return total;
}

2.2 traits和policy的区别

2.3 运用普通的迭代器进行累积

#include <iterator>

template<typename Iter>
auto accum (Iter start, Iter end)
{
    using VT = typename std::iterator_traits<Iter>::value_type;
    VT total{};
    while (start != end) {
        total += *start;
        ++start;
    }
    return total;
}
namespace std {
    template<typename T>
    struct iterator_traits<T*> {
        using difference_type = ptrdiff_t;
        using value_type = T;
        using pointer = T*;
        using reference = T&;
        using iterator_category = random_access_iterator_tag ;
    };
}

03 类型函数(Type Functions)

#include <iostream>

template<typename T>
class TypeSize {
public:
    static const std::size_t value = sizeof(T);
};

int main()
{
    std::cout << TypeSize<int>::value; // 4
}

3.1 确定元素类型

#include <iostream>
#include <vector>
#include <list>

template<typename T>
struct ElementT; // primary template

template<typename T>
struct ElementT<std::vector<T>> { //partial specialization for std::vector
    using Type = T;
};

template<typename T>
struct ElementT<std::list<T>> { // partial specialization for std::list
    using Type = T;
};

template<typename T, std::size_t N>
struct ElementT<T[N]> { // partial specialization for arrays of known bounds
    using Type = T;
};

template<typename T>
struct ElementT<T[]> { // partial specialization for arrays of unknown bounds
    using Type = T;
};

template<typename T>
void printElementType (const T& c)
{
    std::cout << typeid(typename ElementT<T>::Type).name();
}

int main()
{
    std::vector<bool> s;
    printElementType(s); // bool
    int arr[42];
    printElementType(arr); // int
}
template<typename C>
struct ElementT {
    using Type = typename C::value_type;
};
template<typename T1, typename T2, ...>
class X {
  public:
    using ... = T1;
    using ... = T2;
    ...
};
template<typename T, typename C>
T sumOfElements (const C& c);
template<typename C>
typename ElementT<C>::Type sumOfElements (const C& c);
template<typename T>
using ElementType = typename ElementT<T>::Type;

template<typename C>
ElementType<C> sumOfElements (const C& c);

3.2 Transformation traits

3.2.1 移除引用

template<typename T>
struct RemoveReferenceT {
    using Type = T;
};

template<typename T>
struct RemoveReferenceT<T&> {
    using Type = T;
};

template<typename T>
struct RemoveReferenceT<T&&> {
    using Type = T;
};

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

3.2.2 添加引用

template<typename T>
struct AddLValueReferenceT {
    using Type = T&;
};

template<typename T>
using AddLValueReference = typename AddLValueReferenceT<T>::Type;

template<typename T>
struct AddRValueReferenceT {
  using Type = T&&;
};

template<typename T>
using AddRValueReference = typename AddRValueReferenceT<T>::Type;
template<typename T>
using AddLValueReferenceT = T&;

template<typename T>
using AddRValueReferenceT = T&&;
template<>
struct AddLValueReferenceT<void> {
    using Type = void;
};

template<>
struct AddLValueReferenceT<void const> {
    using Type = void const;
};

template<>
struct AddLValueReferenceT<void volatile> {
    using Type = void volatile;
};

template<>
struct AddLValueReferenceT<void const volatile> {
    using Type = void const volatile;
};

3.2.3 移除限定符

template<typename T>
struct RemoveConstT {
    using Type = T;
};

template<typename T>
struct RemoveConstT<T const> {
    using Type = T;
};

template<typename T>
using RemoveConst = typename RemoveConstT<T>::Type;
template<typename T>
struct RemoveCVT : RemoveConstT<typename RemoveVolatileT<T>::Type> {
};

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

3.2.4 Decay

#include <iostream>
#include <type_traits>

template<typename T>
void f(T) {}

template<typename A>
void printParameterType(void (*)(A))
{
    std::cout << "Parameter type: " << typeid(A).name() << '\n';
    std::cout << "- is int: " << std::is_same<A, int>::value << '\n';
    std::cout << "- is const: " << std::is_const<A>::value << '\n';
    std::cout << "- is pointer: " << std::is_pointer<A>::value << '\n';
}

int main()
{
    printParameterType(&f<int>); // 未改变
    printParameterType(&f<int const>); // 衰退为int
    printParameterType(&f<int[7]>); // 衰退为int*
    printParameterType(&f<int(int)>); // 衰退为int(*)(int)
}
#include <iostream>
#include <type_traits>

// 首先定义nonarray,nonfunction的情况,移除任何cv限定符
template<typename T>
struct DecayT : RemoveCVT<T> {
};

// 使用局部特化处理数组到指针的decay,要求识别任何数组类型
template<typename T>
struct DecayT<T[]> {
    using Type = T*;
};
template<typename T, std::size_t N>
struct DecayT<T[N]> {
    using Type = T*;
};

// 函数到指针的decay,必须匹配任何函数类型
template<typename R, typename... Args>
struct DecayT<R(Args...)> {
    using Type = R (*)(Args...);
};
template<typename R, typename... Args>
struct DecayT<R(Args..., ...)> { // 匹配使用C-style vararg的函数类型
    using Type = R (*)(Args..., ...);
};

template<typename T>
using Decay = typename DecayT<T>::Type;

template<typename T>
void printDecayedType()
{
    using A = Decay<T>;
    std::cout << "Parameter type: " << typeid(A).name() << '\n';
    std::cout << "- is int: " << std::is_same<A, int>::value << '\n';
    std::cout << "- is const: " << std::is_const<A>::value << '\n';
    std::cout << "- is pointer: " << std::is_pointer<A>::value << '\n';
}

int main()
{
    printDecayedType<int>();
    printDecayedType<int const>();
    printDecayedType<int[7]>();
    printDecayedType<int(int)>();
}

3.2.5 C++98中的处理

#include <iostream>

template<typename T>
void apply (T& arg, void (*func)(T))
{
    func(arg);
}

void print (int a) { std::cout << a << std::endl; }
void incr (int& a) { ++a; }

int main()
{
    int x = 7;
    apply (x, print); // 1,OK
    apply (x, incr); // 2,error
}
template <typename T>
class TypeOp { // primary template
public:
    typedef T ArgT;
    typedef T BareT;
    typedef T const ConstT;
    typedef T & RefT;
    typedef T & RefBareT;
    typedef T const & RefConstT;
};

template <typename T>
class TypeOp <T const> { // partial specialization for const types
public:
    typedef T const ArgT;
    typedef T BareT;
    typedef T const ConstT;
    typedef T const & RefT;
    typedef T & RefBareT;
    typedef T const & RefConstT;
};

template <typename T>
class TypeOp <T&> { // partial specialization for references
public:
    typedef T & ArgT;
    typedef typename TypeOp<T>::BareT BareT;
    typedef T const ConstT;
    typedef T & RefT;
    typedef typename TypeOp<T>::BareT & RefBareT;
    typedef T const & RefConstT;
};

// 不允许指向void的引用,可以将其看成普通的void类型
template<>
class TypeOp <void> { // full specialization for void
public:
    typedef void ArgT;
    typedef void BareT;
    typedef void const ConstT;
    typedef void RefT;
    typedef void RefBareT;
    typedef void RefConstT;
};

template <typename T>
void apply (typename TypeOp<T>::RefT arg, void (*func)(T))
{
    func(arg);
}

3.3 Promotion Traits(第一版内容)

template<typename T>
Array<T> operator+ (Array<T> const&, Array<T> const&);
template<typename T1, typename T2>
Array<???> operator+ (Array<T1> const&, Array<T2> const&);
template<typename T1, typename T2>
Array<typename Promotion<T1, T2>::ResultT>
operator+ (Array<T1> const&, Array<T2> const&);

// 或使用另一种实现
template<typename T1, typename T2>
typename Promotion<Array<T1>, Array<T2> >::ResultT
operator+ (Array<T1> const&, Array<T2> const&);
template<typename T1, typename T2>
class Promotion;
// traits/ifthenelse.hpp
#ifndef IFTHENELSE_HPP
#define IFTHENELSE_HPP

// primary template: yield second or third argument depending on first argument
template<bool C, typename Ta, typename Tb>
class IfThenElse;

// partial specialization: true yields second argument
template<typename Ta, typename Tb>
class IfThenElse<true, Ta, Tb> {
public:
    typedef Ta ResultT;
};

// partial specialization: false yields third argument
template<typename Ta, typename Tb>
class IfThenElse<false, Ta, Tb> {
public:
    typedef Tb ResultT;
};

#endif // IFTHENELSE_HPP
// traits/promote1.hpp

// primary template for type promotion
template<typename T1, typename T2>
class Promotion {
public:
    typedef typename
        IfThenElse<(sizeof(T1)>sizeof(T2)),
            T1,
            typename IfThenElse<(sizeof(T1)<sizeof(T2)),
                T2,
                void
                >::ResultT
            >::ResultT ResultT;
};
// traits/promote2.hpp

// partial specialization for two identical types
template<typename T>
class Promotion<T,T> {
public:
    typedef T ResultT;
};
// traits/promote3.hpp
#define MK_PROMOTION(T1,T2,Tr) \
    template<> class Promotion<T1, T2> { \
        public: \
            typedef Tr ResultT; \
    }; \
\
    template<> class Promotion<T2, T1> { \
        public: \
            typedef Tr ResultT; \
    };
// traits/promote4.hpp

MK_PROMOTION(bool, char, int)
MK_PROMOTION(bool, unsigned char, int)
MK_PROMOTION(bool, signed char, int)
...
// traits/promotearray.hpp

template<typename T1, typename T2>
class Promotion<Array<T1>, Array<T2> > {
public:
    typedef Array<typename Promotion<T1,T2>::ResultT> ResultT;
};

template<typename T>
class Promotion<Array<T>, Array<T> > {
public:
    typedef Array<typename Promotion<T,T>::ResultT> ResultT;
};

3.4 Predicate Traits

3.4.1 IsSameT

template<typename T1, typename T2>
struct IsSameT {
    static constexpr bool value = false;
};
template<typename T>
struct IsSameT<T, T> {
    static constexpr bool value = true;
};
if (IsSameT<T, int>::value) ...
template<typename T1, typename T2>
constexpr bool isSame = IsSameT<T1, T2>::value;

3.4.2 true_type和false_type

#include <iostream>

template<bool val>
struct BoolConstant {
    using Type = BoolConstant<val>;
    static constexpr bool value = val;
};
using TrueType = BoolConstant<true>;
using FalseType = BoolConstant<false>;

template<typename T1, typename T2>
struct IsSameT : FalseType
{};
template<typename T>
struct IsSameT<T, T> : TrueType
{};

template<typename T>
void fooImpl(T, TrueType)
{
    std::cout << "fooImpl(T,true) for int called\n";
}
template<typename T>
void fooImpl(T, FalseType)
{
    std::cout << "fooImpl(T,false) for other type called\n";
}
template<typename T>
void foo(T t)
{
    fooImpl(t, IsSameT<T,int>{}); // 根据T是否为int选择实现
}

int main()
{
    foo(42); // calls fooImpl(42, TrueType)
    foo(7.7); // calls fooImpl(42, FalseType)
}
template<typename T>
using IsSame = typename IsSameT<T>::Type;
// C++11/14中的定义
namespace std {
    using true_type = integral_constant<bool, true>;
    using false_type = integral_constant<bool, false>;
}

// C++17中的定义
namespace std {
    using true_type = bool_constant<true>;
    using false_type = bool_constant<false>;
}
// 其中bool_constant的定义
template<bool B>
using bool_constant = integral_constant<bool, B>;

3.5 Result Type traits

template<typename T1, typename T2>
Array<typename PlusResultT<T1, T2>::Type>
operator+ (Array<T1> const&, Array<T2> const&);
template<typename T1, typename T2>
Array<PlusResult<T1, T2>>
operator+ (Array<T1> const&, Array<T2> const&);
// traits/plus1.hpp

template<typename T1, typename T2>
struct PlusResultT {
    using Type = decltype(T1() + T2());
};
template<typename T1, typename T2>
using PlusResult = typename PlusResultT<T1, T2>::Type;
class Integer { ... };
Integer const operator+ (Integer const&, Integer const&);
template<typename T1, typename T2>
Array<RemoveCV<RemoveReference<PlusResult<T1, T2>>>>
operator+ (Array<T1> const&, Array<T2> const&);

3.5.1 declval

namespace std {
template<typename T>
    add_rvalue_reference_t<T> declval() noexcept;
}
#include <utility>

template<typename T1, typename T2>
struct PlusResultT {
    using Type = decltype(std::declval<T1>() + std::declval<T2>());
};
template<typename T1, typename T2>
using PlusResult = typename PlusResultT<T1, T2>::Type;

04 SFINAE-based traits

4.1 SFINAE Out函数重载

#include "issame.hpp"
template<typename T>
struct IsDefaultConstructibleT {
private:
    // test() trying substitute call of a default constructor for T passed as U :
    template<typename U, typename = decltype(U())>
    static char test(void*);
    // test() fallback:
    template<typename>
    static long test(...);
public:
    static constexpr bool value
        = IsSameT<decltype(test<T>(nullptr)), char>::value;
};
template<...> static char test(void*);
template<...> static long test(...);
static constexpr bool value
    = IsSameT<decltype(test<...>(nullptr)), char>::value;
IsDefaultConstructibleT<int>::value // yields true
struct S {
    S() = delete;
};
IsDefaultConstructibleT<S>::value // yields false
template<typename T>
struct IsDefaultConstructibleT {
private:
    // ERROR: test() uses T directly:
    template<typename, typename = decltype(T())>
    static char test(void*);
    // test() fallback:
    template<typename>
    static long test(...);
public:
    static constexpr bool value
        = IsSameT<decltype(test<T>(nullptr)), char>::value;
};

4.1.1 其他可选的SFINAE-based traits实现策略

template<...> static char test(void*);
template<...> static long test(...);
enum { value = sizeof(test<...>(0)) == 1 };
using Size1T = char;
using Size2T = struct { char a[2]; };
// 或者
using Size1T = char(&)[1];
using Size2T = char(&)[2];
template<...> static Size1T test(void*); // checking test()
template<...> static Size2T test(...); // fallback
template<...> static Size1T test(int); // checking test()
template<...> static Size2T test(...); // fallback
...
enum { value = sizeof(test<...>(42)) == 1 };

4.1.2 Making SFINAE-based traits Predicate traits

template<...> static std::true_type test(void*); // checking test()
template<...> static std::false_type test(...); // fallback
using Type = decltype(test<FROM>(nullptr));
#include <type_traits>

template<typename T>
struct IsDefaultConstructibleHelper {
private:
    // test() trying substitute call of a default constructor for T passed as U:
    template<typename U, typename = decltype(U())>
    static std::true_type test(void*);
    // test() fallback:
    template<typename>
    static std::false_type test(...);
public:
    using Type = decltype(test<T>(nullptr));
};
template<typename T>
struct IsDefaultConstructibleT :
IsDefaultConstructibleHelper<T>::Type {
};

4.2 SFINAE Out局部特化

#include "issame.hpp"
#include <type_traits> // defines true_type and false_type

// helper to ignore any number of template parameters:
template<typename...> using VoidT = void;

// primary template:
template<typename, typename = VoidT<>>
struct IsDefaultConstructibleT : std::false_type
{
};

// partial specialization (may be SFINAE'd away):
template<typename T>
struct IsDefaultConstructibleT<T, VoidT<decltype(T())>> : std::true_type
{
};
#include <type_traits>
#ifndef __cpp_lib_void_t
namespace std {
    template<typename...> using void_t = void;
}
#endif

4.3 为SFINAE使用泛型lambda

// traits/isvalid.hpp

#include <utility>
// helper: checking validity of f (args...) for F f and Args... args:
template<typename F, typename... Args,
    typename = decltype(std::declval<F>() (std::declval<Args&&>()...))>
std::true_type isValidImpl(void*);

// fallback if helper SFINAE'd out:
template<typename F, typename... Args>
std::false_type isValidImpl(...);

// define a lambda that takes a lambda f and returns whether
// calling f with args is valid
inline constexpr
auto isValid = [](auto f) {
    return [](auto&&... args) {
        return
            decltype(isValidImpl<decltype(f), decltype(args)&&...>(nullptr)) {};
    };
};

// helper template to represent a type as a value
template<typename T>
struct TypeT {
    using Type = T;
};

// helper to wrap a type as a value
template<typename T>
constexpr auto type = TypeT<T>{};

// helper to unwrap a wrapped type in unevaluated contexts
template<typename T>
T valueT(TypeT<T>); // no definition needed
constexpr auto isDefaultConstructible
    = isValid([](auto x) -> decltype((void)decltype(valueT(x))() {});
isDefaultConstructible(type<int>) // true(int是默认可构造的)
isDefaultConstructible(type<int&>) // false(引用不是默认可构造的)
constexpr auto isDefaultConstructible
    = [](auto&&... args) {
        return decltype(
            isValidImpl<
                decltype([](auto x)
                ->
                decltype((void)decltype(valueT(x))())),
                    decltype(args)&&...
                >(nullptr)){};
};
decltype(std::declval<F>()(std::declval<Args&&>()...))>
template<typename T>
using IsDefaultConstructibleT
    = decltype(isDefaultConstructible(std::declval<T>()));
constexpr auto hasFirst
    = isValid([](auto x) -> decltype((void)valueT(x).first) {});

4.4 SFINAE-Friendly traits

#include <utility>
template<typename T1, typename T2>
struct PlusResultT {
    using Type = decltype(std::declval<T1>() + std::declval<T2>());
};

template<typename T1, typename T2>
using PlusResult = typename PlusResultT<T1, T2>::Type;
template<typename T>
class Array {
    ...
};

// declare + for arrays of different element types:
template<typename T1, typename T2>
Array<typename PlusResultT<T1, T2>::Type>
operator+ (Array<T1> const&, Array<T2> const&);
class A {
};
class B {
};
void addAB(Array<A> arrayA, Array<B> arrayB) {
    auto sum = arrayA + arrayB; // ERROR:初始化PlusResultT<A, B>失败
    ...
}
// declare generic + for arrays of different element types:
template<typename T1, typename T2>
Array<typename PlusResultT<T1, T2>::Type>
operator+ (Array<T1> const&, Array<T2> const&);

// overload + for concrete types:
Array<A> operator+(Array<A> const& arrayA, Array<B> const&
arrayB);
void addAB(Array<A> const& arrayA, Array<B> const& arrayB) {
    auto sum = arrayA + arrayB; // ERROR?取决于编译器是否实例化PlusResultT<A,B>
    ...
}
template<typename T1, typename T2>
struct PlusResultT {
    using Type = decltype(std::declval<T1>() + std::declval<T2>());
};
// traits/hasplus.hpp

#include <utility> // for declval
#include <type_traits> // for true_type, false_type, and void_t
// primary template:
template<typename, typename, typename = std::void_t<>>
struct HasPlusT : std::false_type
{ };

// partial specialization (may be SFINAE'd away):
template<typename T1, typename T2>
struct HasPlusT<T1, T2,
    std::void_t<decltype(std::declval<T1>()+ std::declval<T2>())>
    >
: std::true_type
{ };
#include "hasplus.hpp"

template<typename T1, typename T2, bool = HasPlusT<T1, T2>::value>
struct PlusResultT { // primary template, used when HasPlusT yields true
    using Type = decltype(std::declval<T1>() + std::declval<T2>());
};
template<typename T1, typename T2>
struct PlusResultT<T1, T2, false> { // partial specialization, used otherwise
};

05 IsConvertibleT

细节很重要,因此对SFINAE-based traits通用的方法在实践中可能变得更复杂。为了阐述这点,定义一个traits,它可以确定一个给定的类型能否转换为另一个给定的类型——比如如果我们期望一个确定的基类或一个它的派生类。IsConvertibleT traits判断是否能把第一个传递的类型转换为第二个

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

template<typename FROM, typename TO>
struct IsConvertibleHelper {
private:
    // test() trying to call the helper aux(TO) for a FROM passed as F :
    static void aux(TO);
    template<typename F, typename T,
    typename = decltype(aux(std::declval<F>()))>
    static std::true_type test(void*);

    // test() fallback:
    template<typename, typename>
    static std::false_type test(...);

public:
    using Type = decltype(test<FROM>(nullptr));
};

template<typename FROM, typename TO>
struct IsConvertibleT : IsConvertibleHelper<FROM, TO>::Type {
};

template<typename FROM, typename TO>
using IsConvertible = typename IsConvertibleT<FROM, TO>::Type;

template<typename FROM, typename TO>
constexpr bool isConvertible = IsConvertibleT<FROM, TO>::value;
template<...> static std::true_type test(void*);
template<...> static std::false_type test(...);
...
using Type = decltype(test<FROM>(nullptr));
...
template<typename FROM, typename TO>
struct IsConvertibleT : IsConvertibleHelper<FROM, TO>::Type {};
static void aux(TO);
template<typename = decltype(aux(std::declval<FROM>()))>
static char test(void*);
static void aux(TO);
template<typename F, typename = decltype(aux(std::declval<F>()))>
static char test(void*);
static constexpr bool value
    = isSame<decltype(test<FROM>(nullptr)), char>;
IsConvertibleT<int, int>::value // yields true
IsConvertibleT<int, std::string>::value // yields false
IsConvertibleT<char const*, std::string>::value // yields true
IsConvertibleT<std::string, char const*>::value // yields false
template<typename FROM, typename TO, bool = IsVoidT<TO>::value
    ||
IsArrayT<TO>::value
    ||
IsFunctionT<TO>::value>
struct IsConvertibleHelper {
    using Type = std::integral_constant<bool,
        IsVoidT<TO>::value && IsVoidT<FROM>::value>;
};
template<typename FROM, typename TO>
struct IsConvertibleHelper<FROM,TO,false> {
    ... // previous implementation of IsConvertibleHelper here
};

06 检查成员

6.1 检查成员类型

#include <type_traits> // defines true_type and false_type

// helper to ignore any number of template parameters:
template<typename...> using VoidT = void;

// primary template:
template<typename, typename = VoidT<>>
struct HasSizeTypeT : std::false_type
{
};

// partial specialization (may be SFINAE'd away):
template<typename T>
struct HasSizeTypeT<T, VoidT<typename T::size_type>> :
std::true_type
{
};
typename T::size_type
std::cout << HasSizeTypeT<int>::value; // false
struct CX {
    using size_type = std::size_t;
};
std::cout << HasSizeType<CX>::value; // true

6.1.1 处理引用类型

struct CXR {
    using size_type = char&; // Note: type size_type is a reference type
};
std::cout << HasSizeTypeT<CXR>::value; // OK: prints true
std::cout << HasSizeTypeT<CX&>::value; // OOPS: prints false
std::cout << HasSizeTypeT<CXR&>::value; // OOPS: prints false
template<typename T>
struct HasSizeTypeT<T, VoidT<RemoveReference<T>::size_type>>
: std::true_type {
};

6.1.2 插入式类名称

struct size_type {
};
struct Sizeable : size_type {
};
static_assert(HasSizeTypeT<Sizeable>::value,
    "Compiler bug: Injected class name missing");

6.2 检查任意成员类型

// traits/hastype.hpp

#include <type_traits> // for true_type, false_type, and void_t
#define
DEFINE_HAS_TYPE(MemType) \
    template<typename, typename =
std::void_t<>> \
struct
HasTypeT_##MemType \
: std::false_type {
}; \
template<typename
T> \
struct HasTypeT_##MemType<T, std::void_t<typename
T::MemType>> \
: std::true_type { } // ; intentionally skipped
// traits/hastype.cpp

#include "hastype.hpp"
#include <iostream>
#include <vector>

DEFINE_HAS_TYPE(value_type);
DEFINE_HAS_TYPE(char_type);

int main()
{
    std::cout << "int::value_type: "
        << HasTypeT_value_type<int>::value << '\n';
    std::cout << "std::vector<int>::value_type: "
        << HasTypeT_value_type<std::vector<int>>::value << '\n';
    std::cout << "std::iostream::value_type: "
        << HasTypeT_value_type<std::iostream>::value << '\n';
    std::cout << "std::iostream::char_type: "
        << HasTypeT_char_type<std::iostream>::value << '\n';
}

6.3 检查非类型成员

// traits/hasmember.hpp

#include <type_traits> // for true_type, false_type, and void_t
#define
DEFINE_HAS_MEMBER(Member) \
template<typename, typename = std::void_t<>>
\
struct HasMemberT_##Member
\
: std::false_type { };
\
template<typename T>
\
struct HasMemberT_##Member<T,
std::void_t<decltype(&T::Member)>> \
: std::true_type { } // ; intentionally skipped
// traits/hasmember.cpp

#include "hasmember.hpp"
#include <iostream>
#include <vector>
#include <utility>
DEFINE_HAS_MEMBER(size);
DEFINE_HAS_MEMBER(first);
int main()
{
    std::cout << "int::size: "
        << HasMemberT_size<int>::value << '\n';
    std::cout << "std::vector<int>::size: "
        << HasMemberT_size<std::vector<int>>::value << '\n';
    std::cout << "std::pair<int,int>::first: "
        << HasMemberT_first<std::pair<int,int>>::value << '\n';
}

6.3.1 检查成员函数

DEFINE_HAS_MEMBER(begin);
std::cout << HasMemberT_begin<std::vector<int>>::value; // false
// traits/hasbegin.hpp

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

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

// partial specialization (may be SFINAE'd away):
template<typename T>
struct HasBeginT<T, std::void_t<decltype(std::declval<T>().begin())>>
: std::true_type {
};

6.3.2 检查其他表达式

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

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

// partial specialization (may be SFINAE'd away):
template<typename T1, typename T2>
struct HasLessT<T1, T2,
    std::void_t<decltype(std::declval<T1>()<std::declval<T2>())>>
: std::true_type
{
};
decltype(std::declval<T1>() < std::declval<T2>())
HasLessT<int, char>::value // yields true
HasLessT<std::string, std::string>::value // yields true
HasLessT<std::string, int>::value // yields false
HasLessT<std::string, char*>::value // yields true
HasLessT<std::complex<double>,
    std::complex<double>>::value // yields false
template<typename T>
class C
{
    static_assert(HasLessT<T>::value,
        "Class C requires comparable elements");
    ...
};
#include <utility> // for declval
#include <type_traits> // for true_type, false_type, and void_t

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

// partial specialization (may be SFINAE'd away):
template<typename T>
struct HasVariousT<T, std::void_t<decltype(std::declval<T>().begin()),
    typename T::difference_type, typename T::iterator>>
: std::true_type
{
};

6.4 使用泛型lambda检查成员

// traits/isvalid1.cpp

#include "isvalid.hpp"
#include<iostream>
#include<string>
#include<utility>

int main()
{
    using namespace std;
    cout << boolalpha;

    // define to check for data member first:
    constexpr auto hasFirst
        = isValid([](auto x) -> decltype((void)valueT(x).first) {});
    cout << "hasFirst: " << hasFirst(type<pair<int,int>>) << '\n'; // true

    // define to check for member type size_type:
    constexpr auto hasSizeType
        = isValid([](auto x) -> typename
    decltype(valueT(x))::size_type {});

    struct CX {
        using size_type = std::size_t;
    };
    cout << "hasSizeType: " << hasSizeType(type<CX>) << '\n'; // true
    if constexpr(!hasSizeType(type<int>)) {
        cout << "int has no size_type\n";
        ...
    }

    // define to check for <:
    constexpr auto hasLess
        = isValid([](auto x, auto y) -> decltype(valueT(x) < valueT(y)) {});
    cout << hasLess(42, type<char>) << '\n'; // yields true
    cout << hasLess(type<string>, type<string>) << '\n'; // yields true
    cout << hasLess(type<string>, type<int>) << '\n'; // yields false
    cout << hasLess(type<string>, "hello") << '\n'; // yields true
// traits/isvalid2.cpp

#include "isvalid.hpp"
#include<iostream>
#include<string>
#include<utility>

constexpr auto hasFirst
    = isValid([](auto&& x) -> decltype((void)&x.first) {});

template<typename T>
using HasFirstT = decltype(hasFirst(std::declval<T>()));

constexpr auto hasSizeType
    = isValid([](auto&& x)
    -> typename std::decay_t<decltype(x)>::size_type {});

template<typename T>
using HasSizeTypeT = decltype(hasSizeType(std::declval<T>()));

constexpr auto hasLess
    = isValid([](auto&& x, auto&& y) -> decltype(x < y) {
});

template<typename T1, typename T2>
using HasLessT
    = decltype(hasLess(std::declval<T1>(), std::declval<T2>()));

int main()
{
    using namespace std;
    cout << "first: " << HasFirstT<pair<int,int>>::value << '\n'; // true
    struct CX {
        using size_type = std::size_t;
    };
    cout << "size_type: " << HasSizeTypeT<CX>::value << '\n'; // true
    cout << "size_type: " << HasSizeTypeT<int>::value << '\n'; // false
    cout << HasLessT<int, char>::value << '\n'; // true
    cout << HasLessT<string, string>::value << '\n'; // true
    cout << HasLessT<string, int>::value << '\n'; // false
    cout << HasLessT<string, char*>::value << '\n'; // true
}
template<typename T>
using HasFirstT = decltype(hasFirst(std::declval<T>()));
HasFirstT<std::pair<int,int>>::value
上一篇 下一篇

猜你喜欢

热点阅读