写点什么

C++--- 类型萃取 ---is_array && is_enum/is_union/is_class

作者:桑榆
  • 2022-11-26
    广东
  • 本文字数:4020 字

    阅读完需:约 13 分钟

背景

定义在<type_traits>中,用于判断一个类型是否是数组类型,是否是枚举类型,是否是联合类型,是否是非联合类型的类类型,属于基础的类型判断。

代码实现

gcc 官网:https://gcc.gnu.org/

gcc 代码下载:https://mirrors.tuna.tsinghua.edu.cn/help/gcc.git/

gcc 版本代码:gcc-7.5.0(branch 分支)

文件位置:gcc/libstdc++-v3/include/std/type_traits

注意:以下的代码实现省略了一些宏定义部分,实际的代码还是参考 gcc 源码,这里仅用作相关原理分析。

实现分析

is_array

  /// is_array                                                                                                                                             template<typename>    struct is_array    : public false_type { }; 
template<typename _Tp, std::size_t _Size> struct is_array<_Tp[_Size]> : public true_type { };
template<typename _Tp> struct is_array<_Tp[]> : public true_type { };
复制代码

is_array 的普通版本继承了 false_type,默认返回 false 类型,只有匹配到_Tp[_Size]或者_Tp[]是才特化为 true_type,返回 true 类型,说明 is_array 只会识别这两种数组的类型。

is_enum/is_union/is_class

  /// is_enum  template<typename _Tp>    struct is_enum    : public integral_constant<bool, __is_enum(_Tp)>    { };
/// is_union template<typename _Tp> struct is_union : public integral_constant<bool, __is_union(_Tp)> { };
/// is_class template<typename _Tp> struct is_class : public integral_constant<bool, __is_class(_Tp)> { };
复制代码

这三种类型之所以放到一起,是因为他们都是 C++语言的关键字类型,所以它们的识别也是跟整个程序编译过程相关的。可以看到它们的实现继承了 integral_constant 模板,里面包含一个 bool 类型的值,这个值分别由函数__is_enum/__is_union/__is_class 求出。

下面我们就来初步看一下这个__is_enum 函数的调用过程(省略中间的调用过程),我们上面提到的三个内建函数就在 c-common.c 中定义。

实际上在代码语义分析阶段,会对语句进行分析,在这中间的过程中将会调用 cp_parser_primary_expression 函数进行语义分析,同时将相关的关键字进行标识,依次向下调用 cp_parser_trait_expr,在该函数中将 kind 标识为对应的值,最后调用 finish_trait_expr 函数,在该函数中调用 trait_expr_value 计算 bool 值,实际上是使用前面记录的标识判断是否是对应的类型。

鉴于中间的调用过程很复杂,这里我们只是暂时初步的调用流程。实际上可以这样理解,对于这样的关键字类型,在编译阶段就可以计算出来,所以调用了编译阶段提供的内建函数。

//gcc/gcc/c-family/c-common.cconst struct c_common_resword c_common_reswords[] ={...  { "__is_class",   RID_IS_CLASS,   D_CXXONLY },  { "__is_enum",    RID_IS_ENUM,    D_CXXONLY },  { "__is_union",   RID_IS_UNION,   D_CXXONLY },...}//gcc/gcc/cp/parser.cstatic cp_exprcp_parser_primary_expression (cp_parser *parser,                  bool address_p,                  bool cast_p,                  bool template_arg_p,                  bool decltype_p,                  cp_id_kind *idk) {...  /* Peek at the next token.  */  token = cp_lexer_peek_token (parser->lexer);  switch ((int) token->type)    {    ...    case RID_IS_CLASS:    ...    case RID_IS_ENUM:    ...    case RID_IS_UNION:      return cp_parser_trait_expr (parser, token->keyword);}
/* Parse a trait expression.
Returns a representation of the expression, the underlying type of the type at issue when KEYWORD is RID_UNDERLYING_TYPE. */
static treecp_parser_trait_expr (cp_parser* parser, enum rid keyword){ cp_trait_kind kind; ... switch (keyword) { ... case RID_IS_CLASS: kind = CPTK_IS_CLASS; break; ... case RID_IS_ENUM: kind = CPTK_IS_ENUM; break; ... case RID_IS_UNION: kind = CPTK_IS_UNION; break; ... } ... /* Complete the trait expression, which may mean either processing the trait expr now or saving it for template instantiation. */ switch (kind) { ... default: return finish_trait_expr (kind, type1, type2); }}
//gcc/gcc/cp/semantics.ctreefinish_trait_expr (cp_trait_kind kind, tree type1, tree type2){ switch (kind) { ... case CPTK_IS_CLASS: case CPTK_IS_ENUM: case CPTK_IS_UNION: ... break; ... }
return (trait_expr_value (kind, type1, type2) ? boolean_true_node : boolean_false_node); }}
/* Actually evaluates the trait. */
static booltrait_expr_value (cp_trait_kind kind, tree type1, tree type2){ enum tree_code type_code1; tree t;
type_code1 = TREE_CODE (type1); switch (kind) { ... case CPTK_IS_CLASS: return NON_UNION_CLASS_TYPE_P (type1); case CPTK_IS_ENUM: return type_code1 == ENUMERAL_TYPE; case CPTK_IS_UNION: return type_code1 == UNION_TYPE; }}
//gcc/gcc/cp/cp-tree.h
/* Nonzero if T is a class type but not an union. */#define NON_UNION_CLASS_TYPE_P(T) \ (CLASS_TYPE_P (T) && TREE_CODE (T) != UNION_TYPE)
复制代码

使用案例

#include <iostream>#include <type_traits>#include <array>
class ArrayTest{};
struct EnumTest{enum E{};};enum E{};enum class EnumClass:int{};
typedef union{ int a; int b;}UnionTest;
struct StructTest{};class ClassTest{};union UnionClass{class UC{};};
int main(){ std::cout << std::boolalpha; std::cout << "------is_array------" << std::endl; std::cout << "int:" << std::is_array<int>::value << std::endl; std::cout << "int[]:" << std::is_array<int[]>::value << std::endl; std::cout << "int[5]:" << std::is_array<int[5]>::value << std::endl; std::cout << "class ArrayTest:" << std::is_array<ArrayTest>::value << std::endl; std::cout << "class ArrayTest[]:" << std::is_array<ArrayTest[]>::value << std::endl; std::cout << "class ArrayTest[5]:" << std::is_array<ArrayTest[5]>::value << std::endl; std::cout << "std::array<int,5>:" << std::is_array<std::array<int,5>>::value << std::endl;
std::cout << "------is_enum------" << std::endl; std::cout << "EnumTest:" << std::is_enum<EnumTest>::value << std::endl; std::cout << "EnumTest member E:" << std::is_enum<EnumTest::E>::value << std::endl; std::cout << "enum E:" << std::is_enum<E>::value << std::endl; std::cout << "EnumClass:" << std::is_enum<EnumClass>::value << std::endl;
std::cout << "------is_union------" << std::endl; std::cout << "UnionTest:" << std::is_union<UnionTest>::value << std::endl;
std::cout << "------is_class------" << std::endl; std::cout << "StructTest:" << std::is_class<StructTest>::value << std::endl; std::cout << "ClassTest:" << std::is_class<ClassTest>::value << std::endl; std::cout << "ClassTest*:" << std::is_class<ClassTest*>::value << std::endl; std::cout << "ClassTest&:" << std::is_class<ClassTest&>::value << std::endl; std::cout << "const ClassTest:" << std::is_class<const ClassTest>::value << std::endl; std::cout << "EnumClass:" << std::is_class<EnumClass>::value << std::endl; std::cout << "UnionClass:" << std::is_class<UnionClass>::value << std::endl; std::cout << "UnionClass::UC:" << std::is_class<UnionClass::UC>::value << std::endl; std::cout << "incomplete struct IS:" << std::is_class<struct IS>::value << std::endl; std::cout << "incomplete class CS:" << std::is_class<class IS>::value << std::endl;
return 0;}
复制代码

通过上面的例子,我们也能看到如下的几点:

  • is_array

    std::array 并不是数组类型(这里指原生数组),std::array 是 STL 实现的与原生数组类似的功能

    type[]和 type[size]都能表示一个数组

  • is_enum:只能识别枚举类型和枚举类

  • is_union:识别联合类型

  • is_class

    struct 也是类类型,特殊点在于它的成员变量都是 public 的

    类指针,类引用都不是类类型

    const 修饰的类也是类类型

    枚举类不是类类型(class 关键字类似与作用域,使得枚举类型的使用更加安全)

    联合类型不是类类型,但联合类型中定义的类是类类型

    在语句中定义不完整的 struct 和 class 也是类类型。

总结

  • is_array

    std::array 并不是数组类型(这里指原生数组),std::array 是 STL 实现的与原生数组类似的功能

    type[]和 type[size]都能表示一个数组

  • is_enum:只能识别枚举类型和枚举类

  • is_union:识别联合类型

  • is_class

    struct 也是类类型,特殊点在于它的成员变量都是 public 的

    类指针,类引用都不是类类型

    const 修饰的类也是类类型

    枚举类不是类类型(class 关键字类似与作用域,使得枚举类型的使用更加安全)

    联合类型不是类类型,但联合类型中定义的类是类类型

    在语句中定义不完整的 struct 和 class 也是类类型。

发布于: 刚刚阅读数: 5
用户头像

桑榆

关注

北海虽赊,扶摇可接;东隅已逝,桑榆非晚! 2020-02-29 加入

Android手机厂商-相机软件系统工程师 爬山/徒步/Coding

评论

发布
暂无评论
C++---类型萃取---is_array && is_enum/is_union/is_class_C++ STL_桑榆_InfoQ写作社区