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>::valuetrue;但是当 _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)> {
};

但是,如果 Tfinal,则 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); 则不起作用)。