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

时间:2018-10-19 00:18:20

标签: c++ inheritance strict-aliasing object-lifetime placement-new

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

//First chain of actions - Has early priority but a large loop in the middle
add_action( 'init', 'first_chain_first_loop', 10 );
function first_chain_first_loop() {
    for ( $i=0; $i < 10; ++$i ) {
        do_action( 'first_chain_second_loop_action', $i );
    }
}
add_action( 'first_chain_second_loop_action', 'first_chain_second_loop', 10, 1 );
function first_chain_second_loop( $i ) {
    for ( $i=0; $i < 1000000000; ++$i ) {
        do_action( 'first_chain_logic_function_action', $i );
    }
}
add_action( 'first_chain_logic_function_action', 'first_chain_logic_function', 10, 1 );
function first_chain_logic_function( $i ) {
    //Do stuff here
}


//Second chain of actions  - Has later priority
add_action( 'init', 'second_chain_first_loop', 11 );
function second_chain_first_loop() {
    for ( $i=0; $i < 10; ++$i ) {
        do_action( 'second_chain_second_loop_action',  $i );
    }
}
add_action( 'second_chain_second_loop_action', 'second_chain_second_loop', 10, 1 );
function second_chain_second_loop( $i ) {
    for ( $i=0; $i < 10; ++$i ) {
        do_action( 'second_chain_logic_function_action', $i );
    }
}
add_action( 'second_chain_logic_function_action', 'second_chain_logic_function', 10, 1 );
function second_chain_logic_function( $i ) {
    //Do stuff here
}

请注意,我尝试构建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})); } 的结构以不破坏编译器可能做出的任何严格的别名假设。

2 个答案:

答案 0 :(得分:1)

不,出于至少两个原因,C ++标准不允许这样做。

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

  

如果在对象的生存期结束之后且在重新使用或释放​​该对象所占用的存储之前,在原始对象所占用的存储位置上创建了一个指向原始对象的指针的新对象,引用原始对象的引用或原始对象的名称将自动引用新对象,并且一旦开始新对象的生存期,就可以使用它来操作新对象,如果:

     
      
  • 新对象的存储空间正好覆盖了原始对象所占据的存储位置,并且

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

  •   
  • 原始对象的类型不是const限定的,并且,如果是类类型,不包含任何类型为const限定或的非静态数据成员。 >参考类型

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

  •   
  • [C ++ 20草案2018-10-09] 原始对象和新对象都不是潜在重叠的子对象([intro.object])。

  •   

C ++ 20的更改是为了考虑零大小的非静态数据成员的可能性,但是它仍然排除了所有基类子对象(是否为空)的可能性。 “可能重叠的子对象”是在[intro.object] paragraph 7中定义的新术语:

  

可能重叠的子对象为:

     
      
  • 基类子对象,或

  •   
  • 使用no_unique_address属性([dcl.attr.nouniqueaddr])声明的非静态数据成员。

  •   

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

答案 1 :(得分:-1)

static_cast<derived*>(
        ::new (&something) base{ref})

无效,根据定义new (...) base(...)创建一个base对象作为新的完整对象,有时(在某些情况下)可以将其视为现有的完整对象或 member 子对象base仍然无法满足),但永远不会是基础子对象。

没有现有规则,该规则表明您可以假装new (addr) base创建有效的派生对象,只是因为base对象正在覆盖另一个base基础子对象。如果以前有一个derived对象,那么您刚刚将其存储与new (addr) base重用了。即使derived对象仍然存在,对新表达式求值的结果也不会指向它,而是指向一个base完整对象。

如果您想假装自己做了某件事(例如创建一个derived对象),却没有实际做(调用derived构造函数),则可以在指针上倒入一些volatile限定符迫使编译器消除所有关于值的假设,并像执行ABI转换一样编译代码。