libcxx 中 std::is_function 的实现如何工作?

How does the impementation for std::is_function in libcxx work?

libcxx/include/type_traits中,std::is_function是这么紧凑实现的:

namespace __libcpp_is_function_imp
{
struct __dummy_type {};
template <class _Tp> char  __test(_Tp*);
template <class _Tp> char __test(__dummy_type);
template <class _Tp> __two __test(...);
template <class _Tp> _Tp&  __source(int);
template <class _Tp> __dummy_type __source(...);
}

template <class _Tp, bool = is_class<_Tp>::value ||
                            is_union<_Tp>::value ||
                            is_void<_Tp>::value  ||
                            is_reference<_Tp>::value ||
                            __is_nullptr_t<_Tp>::value >
struct __libcpp_is_function
    : public integral_constant<bool,
                               sizeof(__libcpp_is_function_imp::__test<_Tp>(
                                      __libcpp_is_function_imp::__source<_Tp>(0))) == 1>
    {};
template <class _Tp> struct __libcpp_is_function<_Tp, true> : public false_type {};

template <class _Tp> struct _LIBCPP_TEMPLATE_VIS is_function
    : public __libcpp_is_function<_Tp> {};

我大概明白了。 如果类型不匹配任何非函数类型(class、union、void、reference、nullptr_t),则它是函数类型。。但是,我找不到这一行的含义:

sizeof(__libcpp_is_function_imp::__test<_Tp>(__libcpp_is_function_imp::__source<_Tp>(0))) == 1

我想,__libcpp_is_function_imp::__source<_Tp>(0)的结果类型应该是_Tp&。所以 __libcpp_is_function_imp::__test<_Tp>(_Tp&) 的结果类型应该是 _two。而sizeof(_two)应该等于2,与1不同。换句话说,等式 sizeof(__libcpp_is_function_imp::__test<_Tp>(__libcpp_is_function_imp::__source<_Tp>(0))) == 1 总是错误的。

但我一定是搞错了。谁能指出我?

C++ 中的每种类型恰好属于以下类别之一,可能是 cv 限定的:

  • void
  • decltype(nullptr) (a.k.a.std::nullptr_t)
  • 算术
  • 数组
  • 指针(,对于某些类型 T* T
  • 引用(左值或右值)
  • 指向非静态成员的指针
  • 枚举
  • classstruct
  • union
  • 函数

删除 classunionvoid、引用和 std::nullptr_t 后,我们剩下以下可能的类型:

  • 算术
  • 数组
  • 指针
  • 指向非静态成员的指针
  • 枚举
  • 函数

剩余的模板元编程利用了关于这些剩余类别中的类型的两个事实:

  • 如果 _Tpabominable function type,则创建引用类型 _Tp& 的尝试格式错误。否则,_Tp& 是合式的。
  • 否则,类型 _Tp 可以转换为 _Tp* 当且仅当 _Tp 是一个函数类型,通过函数到指针的转换。

留作 reader 的练习以确定为什么 classunionvoid、参考和 std::nullptr_t 类型必须是在该测试正常运行之前的较早阶段被淘汰。