写点什么

C++ 学习 --- 类型萃取 ---is_function

作者:桑榆
  • 2022-11-27
    广东
  • 本文字数:2881 字

    阅读完需:约 9 分钟

背景

定义在<type_traits>中,用于判断一个类型是否是函数类型,这里面包括 std::function,lambda 表达式,重载了调用运算符的类,注意函数指针并不是函数类型,属于基础的类型判断。

代码实现

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 源码,这里仅用作相关原理分析。

实现分析

基础实现

继承 false_type,默认返回 false 常量

  /// is_function  template<typename>    struct is_function    : public false_type { };
复制代码

常规函数识别

这里定义了返回值类型和参数类型,参数类型是 0 个或多个类型,这就是普通的函数类型。

  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes...) _GLIBCXX_NOEXCEPT_QUAL>    : public true_type { };
复制代码

变参函数识别

这个识别与上一条的差异在于,特化参数时增加了一个`...`符号,这个是针对类似 printf 这样的函数的处理,表示可能有多个入参,前面的`_ArgTypes...`表示多个类型

  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes......) _GLIBCXX_NOEXCEPT_QUAL>    : public true_type { };
复制代码

const 和 volatile 限定词识别

const 和 volatile 限定词进行组合有 3 种,再叠加前面两种,共 6 种,标识函数的类型。

实际上这样的函数定义多是成员函数,这时,const 和 volatile 实际是作用到对象的 this 指针上,如类中的 const 修饰函数,说明 this 指针是 const 类型,不可修改类的成员变量。

  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes...) const _GLIBCXX_NOEXCEPT_QUAL>    : public true_type { };
template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM> struct is_function<_Res(_ArgTypes......) const _GLIBCXX_NOEXCEPT_QUAL> : public true_type { }; template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM> struct is_function<_Res(_ArgTypes...) volatile _GLIBCXX_NOEXCEPT_QUAL> : public true_type { }; template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM> struct is_function<_Res(_ArgTypes......) volatile _GLIBCXX_NOEXCEPT_QUAL> : public true_type { }; template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM> struct is_function<_Res(_ArgTypes...) const volatile _GLIBCXX_NOEXCEPT_QUAL> : public true_type { }; template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM> struct is_function<_Res(_ArgTypes......) const volatile & _GLIBCXX_NOEXCEPT_QUAL> : public true_type { };
复制代码

左值引用和右值引用识别

左值引用 &和右值引用 &&再进行叠加,总共有 16 种。

与 const 和 volatile 关键字不同,左值引用和右值引用不影响 this 指针,比如在右值引用修饰的函数中,`*this`返回的还是一个左值引用。

  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes...) & _GLIBCXX_NOEXCEPT_QUAL>    : public true_type { };      template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes......) & _GLIBCXX_NOEXCEPT_QUAL>    : public true_type { };      template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes...) const & _GLIBCXX_NOEXCEPT_QUAL>    : public true_type { };      template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes......) const & _GLIBCXX_NOEXCEPT_QUAL>    : public true_type { };      template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes...) volatile & _GLIBCXX_NOEXCEPT_QUAL>    : public true_type { };      template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes......) volatile & _GLIBCXX_NOEXCEPT_QUAL>    : public true_type { };      template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes...) const volatile & _GLIBCXX_NOEXCEPT_QUAL>                                                                           : public true_type { };          template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>    struct is_function<_Res(_ArgTypes......) const volatile & _GLIBCXX_NOEXCEPT_QUAL>    : public true_type { };
// 右值引用 template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM> struct is_function<_Res(_ArgTypes...) && _GLIBCXX_NOEXCEPT_QUAL> : public true_type { };... template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM> struct is_function<_Res(_ArgTypes......) const volatile && _GLIBCXX_NOEXCEPT_QUAL> : public true_type { };
复制代码

主要的区别在函数重载决议时,左值引用和右值引用修饰的函数表现不同:


  • 无引用修饰符:隐式对象参数调用时默认是左值引用,而且允许绑定右值到隐式对象上;

  • 左值引用:隐式对象参数调用调用时是左值引用;

  • 右值引用:隐式对象参数调用时是右值引用。如下的例子中,A 中只有一个无引用修饰函数,B 中定义了两个同名函数 f(),一个为左值引用,一个为右值引用。

#include <iostream>
struct A{ void f(){std::cout << "no reference!!!\n";};};
struct B{ void f() & {std::cout << "left reference!!!\n";}; void f() && {std::cout << "right reference!!!\n";};};
int main(){ std::cout << "------no split reference------\n"; A a; a.f(); std::move(a).f(); A().f();
std::cout << "------split reference------\n"; B b; b.f(); std::move(b).f(); B().f(); return 0;}
复制代码

执行结果如下:

没有区分时,都调用无引用修饰的函数;区分时,会根据调用的对象引用类型决定调用哪一个函数。

最新的实现方式

只需要判定一个类型不是 const 类型或者引用类型就可以确定该类型是函数类型,使用的是排除法。相比而言,上面的方法更适合初学者理解这个过程。

template<class T>struct is_function : std::integral_constant<bool,    !std::is_const<const T>::value && !std::is_reference<T>::value> {};
复制代码


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

桑榆

关注

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

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

评论

发布
暂无评论
C++学习---类型萃取---is_function_C++ STL_桑榆_InfoQ写作社区