具有用户定义构造函数的普通和POD类型

时间:2012-08-12 15:43:53

标签: c++ c++11 pod standard-layout

首先,这更像是一个健全性检查问题,以获得比我更熟悉语言标准深度的人的一些认可。

假设我有以下类型(虽然我省略了任何非构造函数和非赋值成员函数):

template<typename E> struct half_expr
{
};

class half : public half_expr<half>
{
public:
    half();
    explicit half(float);
    template<typename E> half(const half_expr<E>&);

    half& operator=(float);
    template<typename E> half& operator=(const half_expr<E>&);

private:
    half(std::uint16_t, bool);

    std::uint16_t data_;
};

嗯,在任何合理的实现中,half在内存中不应该是普通的std::uint16_t。但我对标准的保证感兴趣。根据POD的C ++ 98/03定义,这是我的理由:

  • half不能是POD类型,因为它具有非公共字段,基类和用户定义的构造函数。

和C ++ 11失败/扩展定义:

  • 它应该是可以轻易复制的,因为它只隐式生成了复制/移动构造函数/赋值,但只要那些float和模板版本不以任何方式计算,我就是我不完全确定。

  • 它也应该是标准布局,因为它只有一个基本类型的私有字段和一个空的非虚拟基类(在任何标准中都应该是POD,对吗?)

    < / LI>
  • 阻碍POD分类的唯一因素是它不是简单的默认构造,可以通过使用C ++ 11的half() = default语法来克服。

我非常简单的问题只是:我的理由是完全正确还是在定义中有任何我忽略或误解的内容,特别是考虑到用户定义的构造函数和赋值会以某种方式阻碍分类的简单复制?

注意:即使您觉得将此委托给POD和标准布局类型(我可以完全理解)的一些可能重复的冲动,回答我实际问题的简单评论仍然是很好,因为这只是一个简单的检查,可能对你来说简单或超级,但我只是想保持安全。

1 个答案:

答案 0 :(得分:6)

是的,half是标准布局(9/7):基类是空的,派生类对所有非静态数据具有相同的访问控制,没有虚拟的,也没有非标准的 - 布局基础或成员,基础是与第一个非静态数据成员不同的类型。

您定义的构造函数和赋值运算符是复制(或移动)构造函数和赋值,因此它们与该类是否可以轻易复制无关。

默认构造函数是非平凡的,因为它是用户提供的(12.1 / 5),因此该类并不简单。

副本/移动都是微不足道的,因为数据成员和空基都有简单的副本/移动(12.8 / 12)。对于析构函数也是如此,因此该类可以轻易复制。

所以我相信你的分析是正确的 - 删除no-arg构造函数,你在C ++ 11中有一个POD类(但不是在C ++ 98中)。

正如Luc所说,标准中无法保证POD类不包含填充,即使它只有一个数据成员。

在C ++ 03中,空基类优化是允许的,不是必需的,因此质量差但符合C ++ 03的实现肯定会给你sizeof(half) == sizeof(half_expr<half>) + sizeof(uint16_t)。保证sizeof(half_expr<half>) > 0,如果它小于uint16_t,你也可以合理地期望填充。

在C ++ 11中,有效的布局兼容性规则要求应用空基类优化 - 两种标准布局类型,其中一种具有基类而另一种不具有布局兼容性( 9.2 / 17)和布局兼容类型可以通过联合(9.2 / 19)读取,这意味着它们必须具有相同的布局。填充仍然是允许的,但没有意义。

除了标准之外,您的实现可能也可能不是来自C ++ ABI,它可以告诉您POD类的外观。如果是这样,我希望虽然ABI可能不是C ++ 11的最新版本,但是实现将确​​保所有标准布局类看起来像POD类定义的样子,无论它们是否是也是微不足道的(它将进行EBC优化)。这就是标准布局的重点:“类似于C结构的类”。

我认为实现也允许任意决定所有类类型必须是4对齐的,在这种情况下你再次得到填充。我不认为这是合情合理的,因为我能想到的唯一动机就是你在一些奇怪的架构中,4对齐的指针可能小于char*