非连续对象的数组 [英] Array of non-contiguous objects

查看:88
本文介绍了非连续对象的数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

#include <iostream> 
#include <cstring>
// This struct is not guaranteed to occupy contiguous storage
// in the sense of the C++ Object model (§1.8.5):
struct separated { 
  int i; 
  separated(int a, int b){i=a; i2=b;} 
  ~separated(){i=i2=-1;} // nontrivial destructor --> not trivially   copyable
  private: int i2;       // different access control --> not standard layout
};
int main() {
  static_assert(not std::is_standard_layout<separated>::value,"sl");
  static_assert(not std::is_trivial<separated>::value,"tr");
  separated a[2]={{1,2},{3,4}};
  std::memset(&a[0],0,sizeof(a[0]));
  std::cout<<a[1].i;    
  // No guarantee that the previous line outputs 3.
}
// compiled with Debian clang version 3.5.0-10, C++14-standard 
// (outputs 3) 

  1. 削弱标准保证以至该程序可能显示不确定行为的原因是什么?

  1. What is the rationale behind weakening standard guarantees to the point that this program may show undefined behaviour?

标准说: 数组类型的对象包含一个连续分配的N类型的N个子对象的非空集." [dcl.array]§8.3.4. 如果类型T的对象不占用连续存储空间,那么此类对象的数组怎么办?

The standard says: "An object of array type contains a contiguously allocated non-empty set of N subobjects of type T." [dcl.array] §8.3.4. If objects of type T do not occupy contiguous storage, how can an array of such objects do?

删除了可能分散注意力的解释性文字

edit: removed possibly distracting explanatory text

推荐答案

1. 这是巨龙采用的Occam剃刀实例,实际上它是编写编译器的:不要提供超过解决问题所需的更多保证,因为否则,您的工作量将增加一倍,而不会得到补偿.问题的一部分是适用于高档硬件或历史性硬件的复杂类. (由BaummitAugen和M.M提示)

1. This is an instance of Occam's razor as adopted by the dragons that actually write compilers: Do not give more guarantees than needed to solve the problem, because otherwise your workload will double without compensation. Sophisticated classes adapted to fancy hardware or to historic hardware were part of the problem. (hinting by BaummitAugen and M.M)

2. (连续=共享公共边框,依次或依次共享)

2. (contiguous=sharing a common border, next or together in sequence)

首先,不是T类型的对象总是或从不占用连续存储.单个二进制文件中的同一类型可能有不同的内存布局.

First, it is not that objects of type T either always or never occupy contiguous storage. There may be different memory layouts for the same type within a single binary.

[class.derived]§10(8):基类子对象的布局可能不同于...

[class.derived] §10 (8): A base class subobject might have a layout different from ...

这足以使您退后一步,并确信我们的计算机上发生的事情与标准不矛盾.但是,让我们修正这个问题.更好的问题是:

This would be enough to lean back and be satisfied that what is happening on our computers does not contradict the standard. But let's amend the question. A better question would be:

标准是否允许不单独占用连续存储的对象数组,而同时每两个连续的子对象共享一个公共边界?

Does the standard permit arrays of objects that do not occupy contiguous storage individually, while at the same time every two successive subobjects share a common border?

如果是这样,这将严重影响char *算术与T *算术的联系.

If so, this would influence heavily how char* arithmetic relates to T* arithmetic.

根据您是否理解OP标准报价(意味着只有子对象共享一个公共边界,或者在每个子对象内,这些字节也共享一个公共边界),您可能会得出不同的结论.

Depending on whether you understand the OP standard quote meaning that only the subobjects share a common border, or that also within each subobject, the bytes share a common border, you may arrive at different conclusions.

假设第一个,您发现 连续分配"或连续存储"可能仅表示& a [n] ==& a [0] + n(第23.3.2.1节),这是有关子对象地址的陈述,并不意味着该数组位于在单个连续字节序列内.

Assuming the first, you find that 'contiguously allocated' or 'stored contiguously' may simply mean &a[n]==&a[0] + n (§23.3.2.1), which is a statement about subobject addresses that would not imply that the array resides within a single sequence of contiguous bytes.

如果您使用更强的版本,则可能会得出

If you assume the stronger version, you may arrive at the 'element offset==sizeof(T)' conclusion brought forward in T* versus char* pointer arithmetic That would also imply that one could force otherwise possibly non-contiguous objects into a contiguous layout by declaring them T t[1]; instead of T t;

现在如何解决此问题?标准中对sizeof()运算符有一个根本上模棱两可的定义,这似乎是至少在每个体系结构上键入大致相等的布局的时间的事实,现在不再是这种情况了. (新的展示位置如何知道要创建哪种布局?)

Now how to resolve this mess? There is a fundamentally ambiguous definition of the sizeof() operator in the standard that seems to be a relict of the time when, at least per architecture, type roughly equaled layout, which is not the case any more. (How does placement new know which layout to create?)

应用于类时,结果[ofofof()]是该类对象中的字节数,包括将该类型的对象放置在数组中所需的任何填充. [expr.sizeof]§5.3.3(2)

When applied to a class, the result [of sizeof()] is the number of bytes in an object of that class including any padding required for placing objects of that type in an array. [expr.sizeof] §5.3.3 (2)

但是,等等,所需的填充量取决于布局,并且单个类型可能具有多个布局.因此,我们势必会加一粒盐,并在所有可能的布局中采用最小的盐,或者做一些同样任意的事情.

But wait, the amount of required padding depends on the layout, and a single type may have more than one layout. So we're bound to add a grain of salt and take the minimum over all possible layouts, or do something equally arbitrary.

最后,如果这是预期的含义,则数组定义将从char *算术方面的歧义中受益.否则,问题1的答案将相应适用.

Finally, the array definition would benefit from a disambiguation in terms of char* arithmetic, in case this is the intended meaning. Otherwise, the answer to question 1 applies accordingly.

与现在已删除的答案和评论有关的一些评论: 正如中讨论的那样对象占用不连续的存储字节?,实际上存在不连续的对象.此外,天真地设置子对象可能会使包含对象的不相关子对象无效,即使对于完全连续且可复制的对象也是如此:

A few remarks related to now deleted answers and comments: As is discussed in Can technically objects occupy non-contiguous bytes of storage?, non-contiguous objects actually exist. Furthermore, memseting a subobject naively may invalidate unrelated subobjects of the containing object, even for perfectly contiguous, trivially copyable objects:

#include <iostream>
#include <cstring>
struct A {
  private: int a;
  public: short i;
};
struct B :  A {
  short i;
};
int main()
{
   static_assert(std::is_trivial<A>::value , "A not trivial.");
   static_assert(not std::is_standard_layout<A>::value , "sl.");
   static_assert(std::is_trivial<B>::value , "B not trivial.");
   B object;
   object.i=1;
   std::cout<< object.B::i;
   std::memset((void*)&(A&)object ,0,sizeof(A));
   std::cout<<object.B::i;
}
// outputs 10 with g++/clang++, c++11, Debian 8, amd64     

因此,可以想象问题中的内存集可能将a [1] .i设为零,这样程序将输出0而不是3.

Therefore, it is conceivable that the memset in the question post might zero a[1].i, such that the program would output 0 instead of 3.

在极少数情况下,根本不会在C ++对象中使用类似memset的函数. (通常,如果这样做,子对象的析构函数会公然失败.)但是有时有人希望在其析构函数中清除几乎POD"类的内容,这可能是例外.

There are few occasions where one would use memset-like functions with C++-objects at all. (Normally, destructors of subobjects will fail blatantly if you do that.) But sometimes one wishes to scrub the contents of an 'almost-POD'-class in its destructor, and this might be the exception.

这篇关于非连续对象的数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆