【问题标题】:How does the impementation for std::is_function in libcxx work?libc++ 中 std::is_function 的实现如何工作?
【发布时间】:2019-10-21 14:52:06
【问题描述】:

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&lt;_Tp&gt;(0) 的结果类型应该是_Tp&amp;。所以__libcpp_is_function_imp::__test&lt;_Tp&gt;(_Tp&amp;) 的结果类型应该是_two。而sizeof(_two) 应该等于 2,这与1 不同。换句话说,等式sizeof(__libcpp_is_function_imp::__test&lt;_Tp&gt;(__libcpp_is_function_imp::__source&lt;_Tp&gt;(0))) == 1 总是错误的。

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

【问题讨论】:

  • 除非我是盲人,否则sizeof 的行有 3 个开头的&lt;,但只有两个结尾的&gt;...
  • 抱歉我的错字。谢谢你的评论。我现在就编辑它。
  • 你在哪里找到这个定义?我在 libc++ 源代码中看到了something else。它基本上使用clang __is_function 内在函数。
  • 请注意,他们后来想出了一个更聪明的实现(参见patch)。这个想法是引用和函数类型是唯一可以忽略添加 cv 限定的类型。
  • @DanielLangr 我在github.com/llvm/llvm-project/blob/release/9.x/libcxx/include/… 中找到了这个定义,它在release/9.x 分支中。

标签: c++ c++11 typetraits libc++


【解决方案1】:

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

  • void
  • decltype(nullptr)(又名std::nullptr_t
  • 算术
  • 数组
  • 指针(T*对于某些类型T
  • 参考(左值或右值)
  • 指向非静态成员的指针
  • 枚举
  • classstruct
  • union
  • 功能

在消除 classunionvoid、reference 和 std::nullptr_t 之后,我们剩下以下可能的类型:

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

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

  • 如果_Tpabominable function type,则创建引用类型_Tp&amp; 的尝试是错误的。否则,_Tp&amp; 格式正确。
  • 否则,当且仅当 _Tp 是函数类型时,类型 _Tp 才能通过函数到指针的转换转换为 _Tp*

留给读者作为练习来确定为什么必须在早期阶段消除 classunionvoid、参考和 std::nullptr_t 类型,然后此测试才能正常工作。

【讨论】:

  • @Brian 感谢您出色而清晰的回答。所以关键是如果_Tp是一个函数类型,即void (*)(),那么它可以转换为(void (*)())*。这有点违反直觉。有什么令人信服的解释吗?或者这只是一个规则?
  • @yodahaji 不,void() 可以转换为void(*)()void(*)()已经是指针类型,所以不能转换成双指针类型void(**)()。函数到指针的转换起源于 C。
  • 好的,我明白了。谢谢你的回答。
猜你喜欢
  • 2020-04-26
  • 2015-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-19
  • 1970-01-01
  • 2016-05-14
  • 1970-01-01
相关资源
最近更新 更多