背景
定义在<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 { };
复制代码
主要的区别在函数重载决议时,左值引用和右值引用修饰的函数表现不同:
#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> {};
复制代码
评论