对象切片:按值将 Derived 作为 Base 传递 - 安全还是危险?

Object slicing : pass Derived as Base by value - safe or dangerous?

我正在研究 when/why 对象切片很危险。

我读了一篇很棒的 link 关于 what is safe slicing VS dangerous slicing
这是我可以总结的(粗略地说):-

看完后,我在Visual Studio + Resharper(一个VS插件)中创建了一个测试代码。
我认为我的情况 安全 。但是,我在标记行 #1.

处收到警告

possibly unintended object slicing value-initialized from derived class C

class B1{ int field1=0; };  
class B2{ int field2=0; };
class C: public B1,public B2{    };
class Test{
    void f(B1 b){ }                              #2
    void f2(){
        C c;
        f(c);
        //^ possibly unintended object slicing   #1
    }
};

Resharper 的行为与我的信念相矛盾。

可以归纳为table :-

                      |    #2=B1    |    #2=B1&
==================================================
multi-inherit         |    warn*    |   no-warn*    
--------------------------------------------------
inherit only from B1  |  no-warn    |   no-warn     

然而,这是我所期望的:-

                      |    #2=B1    |    #2=B1&
===================================================================
multi-inherit         |     safe*   |    dangerous*    
-------------------------------------------------------------------
inherit only from B1  |     safe    |      safe     

不一致是用*标记的。

我对对象切片的理解有误,还是 Resharper 错了?

No warning when the type is reference (change #2 from B1 to B1&).

当 f 为 f(B1& b) 时,则不会发生切片。预计不会出现警告。

Always no warning when C derived from only B1, no matter #2 is. (B or B&)

当 C 仅派生自 B1 时,其整个状态包含在基础子对象 B1 中。因此实际上没有数据被转换切掉。也许您的 IDE 认为在这种情况下,可能的无意转换不会成为问题。

Do I misunderstand about object slicing

您似乎假设将基引用绑定到对象会分割基子对象。它不是。 "treacherous"(根据链接的答案)切片发生在通过引用分配给对象时:

void f(B1& b){
    b = C{}; // the B2 portion of the argument is sliced off;
             // only B1 portion is assigned. If b refers to a
             // C, then ((C&)b)::B2 remains unmodified.
             // If that was unintentional, then you were affected
             // by the treacherousness 
}

Resharper acts in a contradict way from my belief.

它以正确的方式运行,让我们看看:

Warning when the type is value.

这是针对此代码的:

class B1{ int field1=0; };  
class B2{ int field2=0; };
class C: public B1,public B2{    };
class Test{
    void f(B1 b){ }
    void f2(){
        C c;
        f(c);
      }
};

在函数调用中,从 B1 派生的 C 类型的变量 c 对类型 B1 的变量 b 进行值初始化,将执行签名为 B1(const B1& rhs);B1 的复制构造函数,在该构造函数中(无论是否自动生成)只有存在于 B1 中的字段将从 rhs 中复制,因此其余部分(来自 B2C 的部分)将被切片。

No warning when the type is reference (change #2 from B1 to B1&).

void f(B1& b){ } 

没错,这就是传递多态对象的方式,方法是将对象分配给基本 class 类型的指针或引用。

Always no warning when C derived from only B1, no matter #2 is. (B or B&)

那只是因为 C 没有字段,所以在这种情况下没有切片。尝试将 int n; 添加到 C 并再次显示警告。