使用临时存储区复制琐碎可复制的类型:是否允许?

时间:2019-02-05 09:15:48

标签: c++ language-lawyer c++17

这个问题是对a comment的回答的another question的跟进。


考虑以下示例:

#include <cstring>
#include <type_traits>
#include <cassert>

int main() {
    std::aligned_storage_t<sizeof(void*), alignof(void*)> storage, copy;

    int i = 42;
    std::memcpy(&storage, &i, sizeof(int));

    copy = storage;

    int j{};
    std::memcpy(&j, &copy, sizeof(int));

    assert(j == 42);
}

可行(用于 works 的某些定义)。但是,该标准告诉我们:

  

对于琐碎可复制类型T的任何对象(基类子对象除外),无论该对象是否持有类型T的有效值,组成该对象的基础字节都可以复制到charunsigned charstd​::​byte的数组中。
  如果该数组的内容被复制回该对象,则该对象随后应保留其原始值。 [示例:

#define N sizeof(T)
char buf[N];
T obj;                          // obj initialized to its original value
std::memcpy(buf, &obj, N);      // between these two calls to std​::​memcpy, obj might be modified
std::memcpy(&obj, buf, N);      // at this point, each subobject of obj of scalar type holds its original value
     

-示例]

这:

  

对于任何平凡可复制的类型T,如果两个指向T的指针指向不同的T对象obj1obj2,则两个{{1} }也不是obj1的基类子对象,如果将构成obj2的基础字节复制到obj1中,则obj2随后应保持与{{1}相同的值}。 [示例:

obj2
     

-示例]

在任何情况下,它都提到允许将普通可复制类型复制到缓冲区中,然后再将其复制回原始类型的新实例中。
在上面的示例中,我执行了类似的操作,此外,我还将缓冲区也复制到了新缓冲区中(这与现实情况有些相似)。

在问题顶部的评论中,作者说,这种行为未指定。另一方面,我看不到例如如何通过网络发送obj1并在另一端使用它(如果不允许的话)(将T* t1p; T* t2p; // provided that t2p points to an initialized object ... std::memcpy(t1p, t2p, sizeof(T)); // at this point, every subobject of trivially copyable type in *t1p contains // the same value as the corresponding subobject in *t2p 复制到缓冲区中,然后通过网络,将其作为缓冲区接收,并在int的实例中进行存储-或多或少与我在示例中所做的一样,中间没有网络。

这是我错过的标准的其他一些符号所允许的吗?还是真的指定不足

2 个答案:

答案 0 :(得分:6)

对我来说很好。

您已将obj1的基础字节复制到obj2中。两者都是微不足道的并且是相同类型的。您引用的散文明确允许这样做。

通过也明确允许的重新解释为char*,将这些基础字节临时存储在大小正确且对齐的存储区中的事实似乎并没有改变。它们仍然是“那些字节”。没有规则说复制必须是“直接的”才能满足这样的功能。

的确,这不仅是处理网络传输时的一种完全通用的模式(常规使用当然不能使它自己正确),而且从历史上来讲,将标准设为疯了不去解释(这使我确信确实需要这样做)。

我可以看到会有疑问,因为首先给出了将这些字节复制回原始对象的规则,然后再次给出了将这些字节复制到新对象的规则。但是我无法检测到两种情况之间的任何逻辑差异,因此发现第一个引用的措词在很大程度上是多余的。作者可能只是想弄清楚,这种安全性在两种情况下都是一样的。

答案 1 :(得分:4)

对我来说,这是C ++中最模糊的问题之一。老实说,我从来没有对C ++中的任何东西感到困惑,就像类型调整一样。总是有一个极端的案例似乎没有被涵盖(或未指定,就像您所说的那样)。

但是,从整数to raw memory (char*) is supposed to be allowed for serialization/examination of underlying object进行转换。

有什么解决方案?

单元测试。那是我解决问题的方法。您要做最符合标准的事情,并编写基本单元测试来测试您的特定假设。然后,无论何时编译新版本或移至新的编译器,都将运行单元测试,并验证编译器是否按预期运行。