std::is_empty<T> 如何在 VS2015(或任何编译器)中实现?
How is std::is_empty<T> implemented in VS2015 (or any compiler)?
我当前的问题的灵感来自于试图了解 std::unique_ptr<T, D>
如何利用模板机制来实例化模板 class T*
的大小 D
( deleter 类型)是一个 lambda 函数类型,但是当 D
是一个函数指针类型时它的大小更大(因为 space 需要在 unique_ptr
实例中分配以存储函数指针)。
查看VS2015源码,发现std::unique_ptr
派生自std::_Unique_ptr_base
,后者又声明了一个_Compressed_pair<class _Ty1, class _Ty2, bool = is_empty<_Ty1>::value && !is_final<_Ty1>::value>
类型的数据成员。后一种情况下的类型 _Ty1
是删除器的类型,D
,即上一段中提到的第二个 unique_ptr
模板参数;也就是说,这个问题背后的动机是我将 _Ty1
作为 lambda 类型与 _Ty1
作为函数指针类型进行对比。 (实际上,正在使用 bool
的默认值。)
当 _Ty1
是 lambda 类型的实例时(当 lambda 没有捕获变量因此大小为 0 时),我认识到 is_empty<_Ty1>::value
是 true
;但是当 _Ty1
是函数指针类型时它是 false
。
这让我去探究 std::is_empty
是如何定义的。
呃!
以下是我在VS2015 C++库源代码中可以找到的std::is_empty
的完整实现。
在文件 type_traits
中是这样的:
// TEMPLATE CLASS is_empty
template<class _Ty>
struct is_empty _IS_EMPTY(_Ty)
{ // determine whether _Ty is an empty class
};
...和宏_IS_EMPTY
定义在同一个文件中:
#define _IS_EMPTY(_Ty) \
: _Cat_base<__is_empty(_Ty)>
...此时我的运气用完了,因为我无法在任何地方找到 __is_empty
的定义。我已经 GREP 遍历了整个 VS2015 安装目录(我认为,其中包括所有 C++ 库源代码,在 - 尽管我可能弄错了)。
我想在需要的时候了解 C++ 的内部结构。但是......我坚持这一点,大量谷歌搜索没有揭示答案(尽管我已经看到对内在函数的引用),而且我的挖掘没有......发现任何源代码。
有人能指教一下这种情况吗? std::is_empty<T>
在 VS2015 或任何其他编译器中实际上是如何实现的?
看起来 MSVC++ 提供了一个内在的 __isempty(T)
而不是处理库级实现。由于传递给 std::is_empty<T>
的参数类型 T
可以是 final
,我认为不会有安全的库实现,可能需要编译器帮助。
在我能想到的库中确定类型 T
是否为空的唯一方法是这个(专业化处理非 class
类型,其中 std::is_empty<T>
是不是 true
):
template <bool, typename T>
struct is_empty_aux: T { unsigned long long dummy; };
template <typename T>
struct is_empty_aux<false, T> { unsigned long long dummy[2]; };
template <typename T>
struct is_empty:
std::integral_constant<bool,
sizeof(is_empty_aux<std::is_class<T>::value, T>)
== sizeof(unsigned long long)> {
};
但是,如果 T
是 final
,则 is_empty_aux
中的继承是非法的。虽然可以使用 std::is_final<T>
检测 final
class 的情况,但我看不到确定其对象是否为空的方法。因此,可能需要使用编译器内在函数。无论如何,编译器内在函数对于某些其他类型特征来说肯定是必需的。编译器内部函数 declarations/definitions 由编译器以某种方式神奇地提供:它们通常既不显式声明也不定义。编译器具有关于类型的必要知识,通过内在函数公开这些知识是一种合理的方法。
还 运行 解决了这个问题,并查看了 Ubuntu 14.04 中的 gcc 4.8 headers。其实有一族,像__is_empty
、__is_pod
、__is_polymorphic
等,都是这样使用的
// /usr/include/c++/4.8/type_traits:516
template<typename _Tp>
struct is_empty
: public integral_constant<bool, __is_empty(_Tp)>
{};
它们似乎在不包含任何 C++ headers 的情况下也能正常工作。我试过 g++ 和 clang++ 来编译这段代码
#include <stdio.h>
struct X {};
int main()
{
printf("%d %d %d\n", __is_empty(X), __is_enum(X), __is_class(X));
return 0;
}
我觉得不寻常的是,它们看起来像函数,但实际上将类型作为参数,而不是实例(如果您尝试 X x; __is_empty(x);
则不起作用)。
我当前的问题的灵感来自于试图了解 std::unique_ptr<T, D>
如何利用模板机制来实例化模板 class T*
的大小 D
( deleter 类型)是一个 lambda 函数类型,但是当 D
是一个函数指针类型时它的大小更大(因为 space 需要在 unique_ptr
实例中分配以存储函数指针)。
查看VS2015源码,发现std::unique_ptr
派生自std::_Unique_ptr_base
,后者又声明了一个_Compressed_pair<class _Ty1, class _Ty2, bool = is_empty<_Ty1>::value && !is_final<_Ty1>::value>
类型的数据成员。后一种情况下的类型 _Ty1
是删除器的类型,D
,即上一段中提到的第二个 unique_ptr
模板参数;也就是说,这个问题背后的动机是我将 _Ty1
作为 lambda 类型与 _Ty1
作为函数指针类型进行对比。 (实际上,正在使用 bool
的默认值。)
当 _Ty1
是 lambda 类型的实例时(当 lambda 没有捕获变量因此大小为 0 时),我认识到 is_empty<_Ty1>::value
是 true
;但是当 _Ty1
是函数指针类型时它是 false
。
这让我去探究 std::is_empty
是如何定义的。
呃!
以下是我在VS2015 C++库源代码中可以找到的std::is_empty
的完整实现。
在文件 type_traits
中是这样的:
// TEMPLATE CLASS is_empty
template<class _Ty>
struct is_empty _IS_EMPTY(_Ty)
{ // determine whether _Ty is an empty class
};
...和宏_IS_EMPTY
定义在同一个文件中:
#define _IS_EMPTY(_Ty) \
: _Cat_base<__is_empty(_Ty)>
...此时我的运气用完了,因为我无法在任何地方找到 __is_empty
的定义。我已经 GREP 遍历了整个 VS2015 安装目录(我认为,其中包括所有 C++ 库源代码,在 - 尽管我可能弄错了)。
我想在需要的时候了解 C++ 的内部结构。但是......我坚持这一点,大量谷歌搜索没有揭示答案(尽管我已经看到对内在函数的引用),而且我的挖掘没有......发现任何源代码。
有人能指教一下这种情况吗? std::is_empty<T>
在 VS2015 或任何其他编译器中实际上是如何实现的?
看起来 MSVC++ 提供了一个内在的 __isempty(T)
而不是处理库级实现。由于传递给 std::is_empty<T>
的参数类型 T
可以是 final
,我认为不会有安全的库实现,可能需要编译器帮助。
在我能想到的库中确定类型 T
是否为空的唯一方法是这个(专业化处理非 class
类型,其中 std::is_empty<T>
是不是 true
):
template <bool, typename T>
struct is_empty_aux: T { unsigned long long dummy; };
template <typename T>
struct is_empty_aux<false, T> { unsigned long long dummy[2]; };
template <typename T>
struct is_empty:
std::integral_constant<bool,
sizeof(is_empty_aux<std::is_class<T>::value, T>)
== sizeof(unsigned long long)> {
};
但是,如果 T
是 final
,则 is_empty_aux
中的继承是非法的。虽然可以使用 std::is_final<T>
检测 final
class 的情况,但我看不到确定其对象是否为空的方法。因此,可能需要使用编译器内在函数。无论如何,编译器内在函数对于某些其他类型特征来说肯定是必需的。编译器内部函数 declarations/definitions 由编译器以某种方式神奇地提供:它们通常既不显式声明也不定义。编译器具有关于类型的必要知识,通过内在函数公开这些知识是一种合理的方法。
还 运行 解决了这个问题,并查看了 Ubuntu 14.04 中的 gcc 4.8 headers。其实有一族,像__is_empty
、__is_pod
、__is_polymorphic
等,都是这样使用的
// /usr/include/c++/4.8/type_traits:516
template<typename _Tp>
struct is_empty
: public integral_constant<bool, __is_empty(_Tp)>
{};
它们似乎在不包含任何 C++ headers 的情况下也能正常工作。我试过 g++ 和 clang++ 来编译这段代码
#include <stdio.h>
struct X {};
int main()
{
printf("%d %d %d\n", __is_empty(X), __is_enum(X), __is_class(X));
return 0;
}
我觉得不寻常的是,它们看起来像函数,但实际上将类型作为参数,而不是实例(如果您尝试 X x; __is_empty(x);
则不起作用)。