代码之家  ›  专栏  ›  技术社区  ›  Filipp

C++中派生的新基础子对象的放置

  •  2
  • Filipp  · 技术社区  · 7 年前

    是否定义了放置新的派生的可轻微破坏的基对象的行为?

    struct base { int& ref; };
    struct derived : public base {
        complicated_object complicated;
        derived(int& r, complicated_arg arg) :
                base {r}, complicated(arg) {}
    };
    
    unique_ptr<derived> rebind_ref(unique_ptr<derived>&& ptr,
                                   int& ref) {
        // Change where the `ref` in the `base` subobject of
        // derived refers.
        return unique_ptr<derived>(static_cast<derived*>(
            ::new (static_cast<base*>(ptr.release()) base{ref}));
    }
    

    注意我试着构造 rebind_ref 为了不破坏编译器可能做出的任何严格的别名假设。

    2 回复  |  直到 7 年前
        1
  •  1
  •   aschepler    7 年前

    不,这是不允许的C++标准,至少有两个原因。

    有时允许将新对象放置到同一类型的另一个对象的存储器中的文本可以在 [basic.life], paragraph 8 . 大胆的强调是我的。

    如果在对象的生存期结束之后,并且在重用或释放所占用的存储之前,在原始对象所占用的存储位置创建了新对象、指向原始对象的指针、引用原始对象的引用,或者,原始对象的名称将自动引用新对象,并且在新对象的生存期开始后,可以用于操作新对象,如果:

    • 新对象的存储正好覆盖原始对象占用的存储位置,并且

    • 新对象的类型与原始对象的类型相同(忽略顶级cv限定符),并且

    • 原始对象的类型不是const限定的,如果是类类型, 不包含任何类型为的非静态数据成员 合格施工员或 引用类型

    • [C++ 17 ]原始对象是 最派生对象 类型 T 新对象是 T (也就是说,他们是 不是基类子对象 )

    • [ C++ 20草案2018—10-09 ] 原始对象和新对象都不是可能重叠的子对象 ([简介对象])。

    C++ 20的更改是为了解释零大小非静态数据成员的可能性,但是它还排除了所有基类子对象(空的或不空的)。“潜在重叠子对象”是在 [intro.object] paragraph 7 :

    可能重叠的子对象 是:

    • 基类子对象

    • 声明为 no_unique_address 属性([dcl.attr.nonuiqueadr])。

    (即使您确实找到了某种方法来重新安排内容,以避免引用成员和基类问题,也要记住确保没有人可以定义 const derived 变量,例如将所有构造函数设为私有!)

        2
  •  -1
  •   curiousguy    7 年前
    static_cast<derived*>(
            ::new (&something) base{ref})
    

    根据定义,是无效的 new (...) base(...) 创建 base 对象作为新的完整对象,可以视为现有的完整对象或 成员 子对象有时(在 基础 但绝不是基本子对象。

    没有现有规则 你可以假装 new (addr) base 创建有效的派生对象,因为 基础 对象正在覆盖另一个 基础 基子对象。如果之前有 derived 对象,您刚刚使用 新建(添加)基 . 即使用魔法 派生的 对象仍然存在,对新表达式求值的结果不会指向它,而是指向 基础 完整的对象。

    如果你想假装你做了什么(比如 派生的 对象),而不是实际执行(调用 派生的 构造器),你可以倒一些 volatile 指针上的限定符,以强制编译器删除对值的所有假设,并像有ABI转换一样编译代码。