【问题标题】:How is std::is_empty<T> implemented in VS2015 (or any compiler)?std::is_empty<T> 如何在 VS2015(或任何编译器)中实现?
【发布时间】:2016-02-21 01:43:06
【问题描述】:

D(删除器类型)是 lambda 函数类型,但更大当D 是函数指针类型时的大小(因为需要在unique_ptr 实例中分配空间来存储函数指针)。

查看VS2015源码,发现std::unique_ptr派生自std::_Unique_ptr_base,又声明了_Compressed_pair&lt;class _Ty1, class _Ty2, bool = is_empty&lt;_Ty1&gt;::value &amp;&amp; !is_final&lt;_Ty1&gt;::value&gt;类型的数据成员。在后一种情况下,_Ty1 类型是删除器的类型,D,即上一段中提到的第二个unique_ptr 模板参数;即,这个问题背后的动机是我将_Ty1 是一个 lambda 类型,而 _Ty1 是一个函数指针类型。 (实际上,bool 的默认值正在被使用。)

_Ty1 是 lambda 类型的实例时(当 lambda 没有捕获变量因此大小为 0 时),我认识到 is_empty&lt;_Ty1&gt;::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&lt;T&gt; 是如何在 VS2015 或任何其他编译器中实际实现的?

【问题讨论】:

  • 我不确定,但根据标准,空类的大小为 1 字节。可以衡量类的大小并确定。我还没有真正尝试过实现这个
  • @DavidHaim - 这种推理与 Dietmar Kühl 提供的答案有关。在他回答的示例中,他利用了这样一个事实,即当非空类派生自空类时,空类子对象 does 的大小为 0 - 只有当类是完全为空,包括该类必须具有非零大小的所有基类子对象。

标签: c++ c++11 stl visual-studio-2015 typetraits


【解决方案1】:

看起来 MSVC++ 提供了一个内在的__isempty(T),而不是处理库级别的实现。由于传递给std::is_empty&lt;T&gt; 的参数类型T 可以是final,我认为不可能有安全的库实现,可能需要编译器帮助。

在我能想到的库中确定类型T 是否为空的唯一方法是这样(专业化处理std::is_empty&lt;T&gt; 不是true 的非class 类型):

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&lt;T&gt; 检测final 类的情况,但我看不到确定其对象是否为空的方法。因此,可能需要使用编译器内在函数。无论如何,编译器内在函数对于某些其他类型的特征肯定是必需的。编译器内在函数是编译器以某种方式神奇地提供的声明/定义:它们通常既没有显式声明也没有定义。编译器具有关于类型的必要知识,通过内在函数公开这些知识是一种合理的方法。

【讨论】:

  • 谢谢!不过,我试图理解的模板机制的一个方面是std::is_empty&lt;T&gt; 如何能够成功处理非类类型,例如intvoid(*)()。也就是说std::is_empty&lt;int&gt;std::is_empty&lt;void(*)()&gt;都是有效的。但是,当Tint 或函数指针时,您的小示例代码将无法编译。我知道这对于您在回答中提出的核心观点是偶然的,但我也试图理解模板机制的这一方面。有什么方法可以在这个答案中为T 合并非类类型?
  • @DanNissenbaum:请注意,没有捕获的 lambda 不是函数和/或函数指针:它仍然是类类型,但可以转换为函数指针.
  • 如果类有足够多的相同类型的空基类子对象,这也会中断。例如,给定struct X {}; template&lt;int&gt; struct Y : X {}; template&lt;int... Ns&gt; struct Z : Y&lt;Ns&gt;... {};Z&lt;1,2,3,4,5,6,7,8,9,10&gt;
  • @DanNissenbaum: std::is_class 可以在没有内在的情况下实现,我认为。内置类型很容易检测(这是对数字类型的打字练习,这些类型针对某些特征单独专门化,并对指针和引用类型使用一些专门化)。我记得有一些技巧可以检测enums 和unions。但是,有些类型特征确实需要内在函数,例如,std::has_virtual_destructor
  • @DanNissenbaum 不完全是。 struct A : Z&lt;1, 2&gt; { short i; }; 应该只占用两个字节。一个类有N个同类型的空基类子对象至少要占用N个字节,但如果实际的数据成员至少占用N个字节,那么基类子对象就不需要对最终大小做出贡献。跨度>
【解决方案2】:

还遇到了这个问题,并查看了 Ubuntu 14.04 中的 gcc 4.8 标头。其实有一个家族,像__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++ 头文件的情况下工作。我试过 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); 则不起作用)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-23
    • 1970-01-01
    • 2011-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多