【问题标题】:What is the order of assignments inside a default move constructor?默认移动构造函数中的赋值顺序是什么?
【发布时间】:2017-12-05 09:54:16
【问题描述】:

我目前正在使用 C++ 编写自定义分配器。这个分配器必须通过移动内存和对象来定期对其内存进行碎片整理。这些移位总是向下的,这意味着移动的内存块的地址在移动时总是减少。当旧内存块和新内存块不重叠时,这样做没有问题。如果它们重叠,我首先必须将对象移动到分配器内存之外的临时区域,然后将其移回新的内存块。

如果移动类型的 std::is_trivially_move_constructible 为真,那么如果默认移动构造函数内的赋值顺序定义明确,我可能会将这个额外的移动保存到临时内存块中。这引出了我的问题:分配的顺序是明确定义的还是特定于平台的?

【问题讨论】:

  • 不能手动移动任何这样的对象,例如std::memmove?
  • 您指的是哪些“作业”?
  • 从技术上讲,数据成员是按照它们在类定义中列出的顺序构造的。这可能与它们在内存中的排列顺序相同,也可能不同;该顺序仅在具有相同访问权限的成员之间得到保证。实际上,在 as-if 规则下,一个简单的复制/移动构造函数可能会退化为 memcpy 调用或类似调用。即便如此,编译器理论上可以从前到后,或从后到前,或先复制奇数字节然后复制偶数字节;我不认为这种假设的实现是不合规的。
  • "此分配器必须定期对其内存进行碎片整理,方法是移动内存和对象。" 嗯,你真的不能这样做。除非您完全了解这些对象中的哪些值是指针,哪些不是。仅供参考:指针可以存储在整数值中。如果您移动一个对象,而不调整指向该内存的所有内容的值,那么您已经破坏了某些东西。
  • 使用 memmove 的垃圾收集已被证明很慢。你可能走错了路,小心。

标签: c++ memory move-semantics allocator move-constructor


【解决方案1】:

来自标准(第 15.8.1 节 [class.copy.ctor])

(14) 非联合类 X 的隐式定义的复制/移动构造函数 执行其基地和成员的成员复制/移动。 [ 笔记: 非静态数据成员的默认成员初始化器被忽略。 另见 15.6.2 中的示例。 — 尾注] 的顺序 初始化与基的初始化顺序相同, 用户定义的构造函数中的成员(参见 15.6.2)

点击链接将我们带到 第 15.6.2 节 [class.base.init]

(13.3) 然后,非静态数据成员按照它们的顺序初始化 在类定义中声明(同样不管顺序如何 mem-initializer s)


没有完全回答这个问题,但正如 Igor Tandetnik 在他的回答中所说,将平凡的构造函数转换为 std::memmove 是合法的

【讨论】:

    【解决方案2】:

    [basic.types]/3 对于任何可简单复制的类型T,如果两个指向T 的指针指向不同的T 对象obj1obj2,其中两者都不是obj1obj2 都是基类子对象,如果构成 obj1 的底层字节 (1.7) 被复制到 obj2obj2 将随后保持与 obj1 相同的值。

    所以对于一个可简单复制的类型,简单地复制字节是安全的,例如与memmove。您可以使用std::is_trivially_copyable 测试此特征。

    请注意,平凡可构造是平凡可复制的必要但非充分条件:

    [class]/6可简单复制的类是一个类:

    (6.1) — 其中每个复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符(12.8、13.5.3)要么被删除,要么被删除,

    (6.2) — 具有至少一个未删除的复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符,并且

    (6.3) — 有一个简单的、未删除的析构函数 (12.4)。

    【讨论】:

    • 我指的是这个而不是模糊的“所有编译器都支持 memmove”
    猜你喜欢
    • 2016-09-13
    • 2013-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-04
    • 2020-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多