第19章 Traits的实现(三)

IsConvertibleT

检测成员类型

实现一个traits,判断一个类型是否能够转换为另一个类型

#include <iostream>
#include <string>
#include <type_traits>
#include <utility>

template<typename FROM, typename TO>
class IsConvertibleHelper {
private:
    // test() trying to call the helper aux(TO) for a FROM passed as F
    static void aux(TO);

    // decltype(aux(std::declval<F>())
    // std::declval<F>() 返回一个右值引用
    // SFINAE,如果 aux(std::declval<F>()) 合法,即可以调用aux,则表示可以转换,返回 std::true_type
    template<typename F, typename T, typename = decltype(aux(std::declval<F>()))>
    static std::true_type test(void*); 

    //  fallback:
    // 如果 aux(std::declval<F>()) 不合法,则表示不可以转换,返回 std::false_type
    template<typename, typename>
    static std::false_type test(...);
public:
    using Type = decltype(test<FROM, TO>(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;

int main() {
    std::cout << std::boolalpha;
    std::cout << "int -> double: " << isConvertible<int,double><< std::endl;
    std::cout << "int -> std::string: " << IsConvertibleT<int, std::string>::value << std::endl;
    std::cout << "char const* -> std::string: " << IsConvertibleT<char const*, std::string>::value<< std::endl;
    std::cout << "std::string -> char const*: " << isConvertible<std::string, char const*><< std::endl;
    // std::cout << "int -> int: " << isConvertible<int, int> << std::endl;
    // std::cout << "int -> int&: " <<isConvertible<int, int&> << std::endl;
}

上面的实现还有一些不足,没有考虑到类似数组等类型的匹配问题,细节可以参考std::is_convertible<>的实现。

Detecting Members 检测成员

给定一个类型T,查看是否有成员类型 size_type:

#include <iostream>
#include <string>
#include <type_traits>
#include <utility>

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

// primary template: 不匹配情况
template<typename, typename = VoidT<>>
struct HasSizeTypeT : std::false_type {};

// 偏特化:匹配情况 (may be SFINAE’d away)
// 在模板参数中声明T的成员T::size_type
// 根据SFINAE的特性,如果存在该成员类型,这个偏特化将会被选中,否则将会被忽略。
template<typename T>
struct HasSizeTypeT<T, VoidT<typename T::size_type>> : std::true_type {};

class A {
public:
    using size_type = std::size_t; // 外部可访问
};

class B {
private:
    using size_type = std::size_t; // 外部不可访问
};

int main() {
    std::cout << std::boolalpha;
    std::cout << "A has size_type: " << HasSizeTypeT<A>::value << '\n';
    std::cout << "B has size_type: " << HasSizeTypeT<B>::value << '\n';
    std::cout << "std::string has size_type: " << HasSizeTypeT<std::string>::value << '\n';
    std::cout << "int has size_type: " << HasSizeTypeT<int>::value << '\n';
}

注意一个细节,如果size_type是私有的,那么template也无法访问,最终返回false.
所以这个trast的语义是判断是否有可访问的成员类型size_type

Note that if the member type size_type is private, HasSizeTypeT yields false because our traits templates have no special access to their argument type, and therefore typename T::size_type is invalid

但是,当传入的是一个引用类型时,最终会返回false

#include <iostream>
#include <string>
#include <type_traits>
#include <utility>

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

// primary template: 不匹配情况
template<typename, typename = VoidT<>>
struct HasSizeTypeT : std::false_type {};

// 偏特化:匹配情况 (may be SFINAE’d away)
// 在模板参数中声明T的成员T::size_type
// 根据SFINAE的特性,如果存在该成员类型,这个偏特化将会被选中,否则将会被忽略。
template<typename T>
struct HasSizeTypeT<T, VoidT<typename T::size_type>> : std::true_type {};

class A {
public:
    using size_type = std::size_t;
};

int main() {
    std::cout << std::boolalpha;
    std::cout << "A has size_type: " << HasSizeTypeT<A>::value << '\n';
    std::cout << "A& has size_type: " << HasSizeTypeT<A&>::value << '\n';
}


这本身没什么问题,引用类型本身并不包含成员(例如,int& 类型没有像 int 类型那样的大小成员)。但在实际使用引用时,引用表达式的值和行为体现的是底层类型(被引用类型)的性质。因此,在某些情况下,尤其是在检测一个类型是否拥有特定成员类型时,从实用角度出发,可能更倾向于考察引用所指向的底层类型是否拥有这个成员类型。当遇到引用类型时,应当考虑去除引用后其底层类型是否满足条件。

#include <iostream>
#include <string>
#include <type_traits>
#include <utility>

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

// primary template: 不匹配情况
template<typename, typename = VoidT<>>
struct HasSizeTypeT : std::false_type {};


// 偏特化版本:先移除引用,再查找size_type成员类型
// template<typename T>
// struct HasSizeTypeT<T, VoidT<typename std::remove_reference<T>::type::size_type>> : std::true_type {};

// 与上面等价
template<typename T>
struct HasSizeTypeT<T, VoidT< typename std::remove_reference_t<T>::size_type>> : std::true_type {};

class A {
public:
    using size_type = std::size_t;
};

int main() {
    std::cout << std::boolalpha;
    std::cout << "A has size_type: " << HasSizeTypeT<A>::value << '\n';
    std::cout << "A& has size_type: " << HasSizeTypeT<A&>::value << '\n';
    std::cout << "A&& has size_type: " << HasSizeTypeT<A&&>::value << '\n';
}

检测任意成员类型

一种实现方式是使用宏(macros),

#include <iostream>
#include <string>
#include <type_traits>

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

// 使用宏来实现对任意类型的检测
// 用于生成一个模板结构体 HasTypeT_MemType,
// 这个结构体的作用是用来检测一个类型 T 是否具有指定名称的成员类型 MemType。
// 这里的 MemType 是一个占位符,将在实际使用时替换为具体的成员类型名称。
// ##MemType 表示这地方会被替换为具体的成员类型名称。
#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 std::remove_reference_t<T>::MemType>> : std::true_type { } 

class A {
public:
    using size_type = std::size_t;
    using value_type = int;
    // no iterator
};

DEFINE_HAS_TYPE(size_type); // 定义一个 HasTypeT_size_type 类型,判断 A 是否有 size_type 类型成员。
DEFINE_HAS_TYPE(value_type); // 定义一个 HasTypeT_value_type 类型,判断 A 是否有 value_type 类型成员。
DEFINE_HAS_TYPE(iterator); // 定义一个 HasTypeT_iterator 类型,判断 A 是否有 iterator 类型成员。
DEFINE_HAS_TYPE(first); // 
int main() {
    std::cout << std::boolalpha;
    std::cout << "A has size_type: " << HasTypeT_size_type<A>::value << '\n';
    std::cout << "A has value_type: " << HasTypeT_value_type<A>::value << '\n';
    std::cout << "A has iterator: " << HasTypeT_iterator<A>::value << '\n';
    std::cout << "A& has size_type: " << HasTypeT_size_type<A&>::value << '\n';
    std::cout << "A& has value_type: " << HasTypeT_value_type<A&>::value << '\n';
    std::cout << "A& has iterator: " << HasTypeT_iterator<A&>::value << '\n';
}

使用宏来生成对应的traits。

检测成员函数

类似于检测成员类型,在模板参数中使用std::declval()构造一个右值对象,并调用begin(), 根据SFINAE的规则进行匹配,不同与成员类型,这里引用包含了函数成员,所以不用对类型进行转换。

#include <iostream>
#include <string>
#include <type_traits>
#include <vector>

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

// primary template: 不匹配情况
template<typename, typename = VoidT<>>
struct HasBeginFunc : std::false_type {};


// 判断begin函数是否声明
template<typename T>
struct HasBeginFunc<T, VoidT<decltype(std::declval<T>().begin())>> : std::true_type {};

class A {
public:
    void begin();
};

int main() {
    std::cout << std::boolalpha;
    std::cout << HasBeginFunc<A>::value << std::endl;
    std::cout << HasBeginFunc<A&>::value << std::endl;
    std::cout << HasBeginFunc<A&&>::value << std::endl;
    std::cout << HasBeginFunc<int>::value << std::endl;
    std::cout << HasBeginFunc<std::string>::value << std::endl;
    std::cout << HasBeginFunc<std::vector<int>>::value << std::endl;
    return 0;
}

检测其他表达式

结合上述方法,可以扩展为对很多表达式的检测,比如可以构造trait,用于检测类型T1 和 类型T2 之间的 < 比较符号是否合法:

#include <iostream>
#include <string>
#include <type_traits>
#include <vector>

// helper to ignore any number of template parameters:
// 其实就是 std::void_t
template<typename...>
using VoidT = void;

// primary template: 不匹配情况
template<typename, typename, typename = VoidT<>>
struct HasLessT : std::false_type {};

// 判断 < 操作符是否是配T1 和 T2 类型
template<typename T1, typename T2>
struct HasLessT<T1, T2, 
    VoidT<decltype(std::declval<T1>() < std::declval<T2>())> > : std::true_type {};

class A {
public:
    void begin();
};

int main() {
    std::cout << std::boolalpha;
    std::cout << HasLessT<int,char>::value << std::endl;
    std::cout << HasLessT<int,double>::value << std::endl;
    std::cout << HasLessT<A,int>::value << std::endl;
    return 0;
}

上述检查的traits, 均可以使用lambda的方式改写:

// define to check for <: 
constexpr auto hasLess 
  = isValid([](auto x, auto y) -> decltype(valueT(x) < valueT(y)) {
            });
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值