zoukankan      html  css  js  c++  java
  • SFINAE 与 type_traits

    SFINAE 与 type_traits

    SFINAE

    替换失败不是错误 (Substitution Failure Is Not An Error),此特性被用于模板元编程。

    在函数模板的重载决议中应用此规则,当将模板形参替换为显式指定的类型或推导的类型失败时,从重载集中丢弃这个特化,而非导致编译失败

    type_traits

    类型特性 定义一个编译时基于模板的结构,以查询或修改类型的属性,是一种类型萃取技术。

    type_traits 在 C++ 中是基于 SFINAE 实现的,通过模板的偏特化来查询或者修改类型的属性,无运行时开销。

    其核心是整数常量模板 integral_constant,MSVC 的实现如下:

    template <class _Ty, _Ty _Val>
    struct integral_constant { // convenient template for integral constant types
      static constexpr _Ty value = _Val;
    
      using value_type = _Ty;
      using type = integral_constant;
    
      constexpr operator value_type() const noexcept { // return stored value
        return (value);
      }
      constexpr value_type operator()() const noexcept { // return stored value
        return (value);
      }
    };
    

    其实平平无奇,保存了类模板的类型和值而已,没有什么特殊的地方,现在将这个模板特化为bool类型

    // ALIAS TEMPLATE bool_constant
    template <bool _Val> using bool_constant = integral_constant<bool, _Val>;
    
    using true_type = bool_constant<true>;
    using false_type = bool_constant<false>;
    

    bool_constant 的实参只能是 bool 类型, true_type 的 integral_constant::value 是true,false_type 的 value 是 false,构建 traits 的基础已经完成。

    常见的几种 traits 的 SFINAE 应用

    像 std::enable_if, is_same, remove_const, remove_reference 等这些是最常用的 traits,STL的代码中这些到处可见。

    enable_if

    根据编译期布尔常量隐藏一个函数重载或模板特化。

    // STRUCT TEMPLATE enable_if
    template <bool _Test, class _Ty = void>
    struct enable_if { // type is undefined for assumed !_Test
    };
    
    template <class _Ty> struct enable_if<true, _Ty> { // type is _Ty for _Test
      using type = _Ty;
    };
    
    template <bool _Test, class _Ty = void>
    using enable_if_t = typename enable_if<_Test, _Ty>::type;
    

    若 B 为 _Test ,则 std::enable_if 拥有等同于 T 的公开成员 typedef type (第二个模板实参),否则,无该成员 typedef。

    is_same

    检测两个参数类型是否相同,如果相同特供 value 为 true,否则为false。实现简单,相同参数的继承 true_type, 不同参数的继承 false_type

    // STRUCT TEMPLATE is_same
    template <class _Ty1, class _Ty2>
    struct is_same
        : false_type { // determine whether _Ty1 and _Ty2 are the same type
    };
    
    template <class _Ty1>
    struct is_same<_Ty1, _Ty1>
        : true_type { // determine whether _Ty1 and _Ty2 are the same type
    };
    
    template <class _Ty, class _Uty>
    constexpr bool is_same_v = is_same<_Ty, _Uty>::value;
    

    remove_const

    控制给定类型限定符是否存在。volatile 同理。

    // STRUCT TEMPLATE remove_const
    template <class _Ty> struct remove_const { // remove top level const qualifier
      using type = _Ty;
    };
    
    template <class _Ty>
    struct remove_const<const _Ty> { // remove top level const qualifier
      using type = _Ty;
    };
    
    template <class _Ty> using remove_const_t = typename remove_const<_Ty>::type;
    

    remove_reference

    从给定类型移除引用,add_*value_reference 实现类似。

    // STRUCT TEMPLATE remove_reference
    template <class _Ty> struct remove_reference { // remove reference
      using type = _Ty;
    };
    
    template <class _Ty> struct remove_reference<_Ty &> { // remove reference
      using type = _Ty;
    };
    
    template <class _Ty>
    struct remove_reference<_Ty &&> { // remove rvalue reference
      using type = _Ty;
    };
    
    template <class _Ty>
    using remove_reference_t = typename remove_reference<_Ty>::type;
    

    其他需要编译器外挂的 traits

    像上面这些都是可以通过库来实现的 traits,这种一般也比较简单的。其他的和类 枚举 虚函数 这些相关一般都要通过内建支持。

    最先想到的就是 is_trivial,这种要通过库来实现就不可能了:

    1. 没有虚函数或虚基类;
    2. 由C++编译器提供默认的特殊成员函数(默认的构造函数、拷贝构造函数、移动构造函数、赋值运算符、移动赋值运算符和析构函数),并且最少有一个未弃置;
    3. 数据成员同样需要满足条件 12
    // STRUCT TEMPLATE is_trivial
    template <class _Ty>
    struct is_trivial
        : bool_constant<__is_trivial(_Ty)> { // determine whether _Ty is trivial
    };
    

    __is_trivial 就是编译器的外挂了,traits 的这种实现一直秉持着能够库实现,绝不编译器去做的原则。

    参考

    1. SFINAE, cppreference.com.
    2. std::is_trivial, cppreference.com.
  • 相关阅读:
    Oracle EXP
    Using Spring in Web and WinForms
    System.ComponentModel(未完...)
    工作必须得到强势方的支持!
    book.Save()还是bookManager.Save(book)?
    C#中常用的Attribute搜集(刚开始...)
    领域模型是否能够屏蔽数据库?
    合格代码的最基本的标准
    关于配置系统的设计
    对象分类
  • 原文地址:https://www.cnblogs.com/shuqin/p/12263178.html
Copyright © 2011-2022 走看看