yodahaji
yodahaji

Reputation: 887

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

In libcxx/include/type_traits, std::is_function is implemented in such a compact way:

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> {};

I got the general idea. If a type does not match any of the non-function type (class, union, void, reference, nullptr_t), it is a function type.. However, I can't find the meaning for this line:

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

I think, the result type for __libcpp_is_function_imp::__source<_Tp>(0) should be _Tp&. So the result type for __libcpp_is_function_imp::__test<_Tp>(_Tp&) should be _two. And sizeof(_two) should equal to 2, which is different from 1. In other words, the equation sizeof(__libcpp_is_function_imp::__test<_Tp>(__libcpp_is_function_imp::__source<_Tp>(0))) == 1 is always false.

But I must get something wrong. Could anyone point me out?

Upvotes: 4

Views: 153

Answers (1)

Brian Bi
Brian Bi

Reputation: 119382

Every type in C++ falls into exactly one of the following categories, possibly cv-qualified:

  • void
  • decltype(nullptr) (a.k.a. std::nullptr_t)
  • Arithmetic
  • Array
  • Pointer (i.e., T* for some type T)
  • Reference (lvalue or rvalue)
  • Pointer to non-static member
  • Enumeration
  • class or struct
  • union
  • Function

After eliminating class, union, void, reference, and std::nullptr_t we are left with the following possible types:

  • Arithmetic
  • Array
  • Pointer
  • Pointer to non-static member
  • Enumeration
  • Function

The remaining template metaprogramming exploits two facts about the types in these remaining categories:

  • If _Tp is an abominable function type, then the attempt to create the reference type _Tp& is ill-formed. Otherwise, _Tp& is well-formed.
  • Otherwise, the type _Tp is convertible to _Tp* if and only if _Tp is a function type, via the function-to-pointer conversion.

It's left as an exercise for the reader to determine why class, union, void, reference, and std::nullptr_t types had to be eliminated at an earlier stage before this test would work correctly.

Upvotes: 4

Related Questions