【问题标题】:Standard layout and non-copyable property标准布局和不可复制的属性
【发布时间】:2012-02-23 01:52:28
【问题描述】:

C++11,第 9/7 节:

一个标准布局类是一个类:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
  • 没有虚函数,也没有虚基类,
  • 对所有非静态数据成员具有相同的访问控制,
  • 没有非标准布局基类,
  • 要么在派生最多的类中没有非静态数据成员,并且最多有一个具有非静态数据成员的基类,要么没有具有非静态数据成员的基类,并且
  • 没有与第一个非静态数据成员相同类型的基类。

那么,有没有办法使具有标准布局的类不可复制?如果是,怎么做?

从 boost::noncopyable 私有继承将不起作用,因为它使复制构造函数私有(因此不是标准布局)。 boost::noncopyable 的实现是这样的:

  class noncopyable
  {
   protected:
      noncopyable() {}
      ~noncopyable() {}
   private:  // emphasize the following members are private
      noncopyable( const noncopyable& );
      const noncopyable& operator=( const noncopyable& );
  };

由于私有部分,它不是标准的布局类。我还注意到私有继承是否违反任何标准布局规则。


#include <boost/noncopyable.hpp>
#include <iostream>
const int N = 50;
struct A
{
    int data[N];
};
struct B : private boost::noncopyable
{
    int data[N];
};
struct C
{
    A data[10];
};
struct D : private boost::noncopyable
{
    B data[10];
};

int main() {
    std::cout<<sizeof(A)<<std::endl;
    std::cout<<sizeof(B)<<std::endl;

    std::cout<<sizeof(C)<<std::endl;
    std::cout<<sizeof(D)<<std::endl;
}

输出是:

200
200
2000
2004

上面的例子表明,从boost::noncopyable 私有继承会将类更改为不符合标准布局。 我不确定这是否是一个 g++ 错误(我使用的是 g++ 4.6.1),或者该标准被某种方式违反了。

【问题讨论】:

  • 你为什么认为将复制构造函数设为私有会使类不是标准布局?
  • @Mankarse 那么,标准布局类可以有私有复制构造函数吗?
  • 函数不是“非静态数据成员”。因此,假设您正确引用了定义,“私人部分”不会使 noncopyable 非标准布局。
  • 我添加了一个示例,其中从 boost::noncopyable 继承的类包括标准布局类(再次从 boost::noncopyable 继承)不是标准布局类。你能解释一下吗?
  • @VJovic :类型增加的大小与标准布局正交(第 9.2/20 节特别允许)——GCC 只是没有应用空基优化。如果你想检查某个东西是否是标准布局,不要看大小,使用它的类型特征 (std::is_standard_layout&lt;&gt;)。

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


【解决方案1】:

我认为这里有一个混淆:

  • 标准布局属性受属性(且仅属性)影响
  • 可复制属性受方法(它们的存在、不存在和可访问性)影响

这两个概念是正交的。

更新:

以下显示与boost::noncopyable 完全相同的行为:

#include <iostream>

struct foo {};

struct B : foo { int data; };

struct D : foo { B data; };

int main() {
  D d;
  std::cout << (char*)(&d.data) - (char*)(&d) << "\n";
}

结果是4

我相信这是因为:

  • 没有与第一个非静态数据成员相同类型的基类。

确实,实验表明在D 中在data 之前引入int a; 不会增加其大小。我认为B 继承自foo 的事实意味着data(第一个非静态数据成员)被认为与fooD 的基类)具有相同的类型。

这会导致歧义:如果编译器没有引入此填充,foo* f = &amp;d 将与 foo* g = &amp;b.data; 具有相同的地址。

【讨论】:

  • 好的,所以从 boost::noncopyable 私有继承也会使其成为标准布局类(假设所有成员变量都是公共的),因为 boost::noncopyable 没有成员数据
  • 我设法创建了一个创建非标准布局类的示例。您是否会以解释示例中行为的方式扩展您的答案?
  • @VJovic:我认为这与BD 都继承自同一个类这一事实有关。我可以确认 Clang 3.0 至少还在 D 的开头引入了 4 位偏移量。我正在检查它,但是用foo 替换boost::noncopyable,其中struct foo {}; 产生相同的效果。
  • @VJovic:我只提供我的想法,我在回答中总结了这些想法。这绝对不是权威,而且很可能是错误的。
【解决方案2】:

您需要做两件事才能使您的课程不可复制:

  1. 将复制构造函数设为私有。
  2. 将赋值运算符设为私有。 (它被分配了与类本身相同类型的另一种类型)。

您不需要为了获得该行为而从某个 boost 类继承。

我可以补充一下,谁在乎一些花哨的“标准布局”想法。编程你需要的东西,不要屈服于这种极端的空间理论。

【讨论】:

  • 标准布局不是“极端空间理论”。这是对 POD 的一种放松,它是(例如)使用offsetof 的要求。更一般地说,这是您想要的用于"extern C" 接口的结构。如果您需要 C 接口,那么您需要标准布局类型。如果你觉得这些东西对你来说太“花哨”了,那只是意味着你可以用 C++ 做某些事情,但是你选择不学着去做。
  • 同样从一些 boost 类继承 1) 减少输入,2) 捕获 all 在编译时复制/分配的尝试(否则类本身和它的朋友可以复制/分配它好吧)。
  • @visitor: 使用 C++11 和 = deleteboost::noncopyable 确实失去了一些吸引力:)
  • “否则类本身和它的朋友可以复制/分配它好吗” - 不是真的,因为如果你不使用boost::noncopyable,C++03 习惯用法是声明复制ctor/分配但不定义它们。所以这个类和它的朋友也不能复制它,只是你直到链接时才发现错误,而任何无法访问私有成员的人在编译时才发现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-08
  • 2012-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
相关资源
最近更新 更多