C++ 如何在对象切片时生成编译器警告/错误

C++ 如何在对象切片时生成编译器警告/错误,c++,compiler-construction,truncate,slice,C++,Compiler Construction,Truncate,Slice,我想知道是否有可能让编译器对以下代码发出警告/错误: 注意: 一,。是的,这是一种糟糕的编程风格,我们应该避免这种情况——但我们正在处理遗留代码,希望编译器能够帮助我们识别这种情况。) 二,。我更喜欢使用编译器选项(VC++)来禁用或启用对象切片(如果有的话) 这里,如果我注释掉第二个函数,将调用第一个函数——编译器(VC++和Gcc)对此感到满意 是C++标准吗?当遇到这样的代码时,我能要求编译器(VC++)给我一个警告吗 非常感谢 编辑: 非常感谢你们的帮助 我找不到一个编译器选项来给出错误

我想知道是否有可能让编译器对以下代码发出警告/错误:

注意:

一,。是的,这是一种糟糕的编程风格,我们应该避免这种情况——但我们正在处理遗留代码,希望编译器能够帮助我们识别这种情况。)

二,。我更喜欢使用编译器选项(VC++)来禁用或启用对象切片(如果有的话)

这里,如果我注释掉第二个函数,将调用第一个函数——编译器(VC++和Gcc)对此感到满意

是C++标准吗?当遇到这样的代码时,我能要求编译器(VC++)给我一个警告吗

非常感谢

编辑:

非常感谢你们的帮助

我找不到一个编译器选项来给出错误/警告-我甚至在MSDN论坛上为VC++编译器顾问发布了这篇文章,但没有回答。所以我担心gcc和vc++都没有实现这个功能

因此,添加以派生类为参数的构造函数将是目前最好的解决方案

编辑

我已向MS提交了一份feedbak,希望他们能尽快修复:

-白岩

这是一个众所周知的问题,它有自己的维基百科文章(尽管它只是对这个问题的一个简短描述)


我相信我使用了一个编译器,它有一个警告,您可以启用它来检测和警告这一点。然而,我记不起是哪一个了。

不是解决你眼前问题的真正办法,但是

大多数将类/结构对象作为参数的函数都应该声明参数的类型为“const X&”或“X&”,除非它们有很好的理由不这样做

如果您总是这样做,对象切片将永远不会成为问题(引用不会被切片!)


你是说派生函数是一个基,所以它应该在任何有基的函数中工作。如果这是一个真正的问题,那么继承可能不是您真正想要使用的

我建议在基类中添加一个构造函数,该构造函数显式地接受对派生类的const引用(带有向前声明)。在我的简单测试应用程序中,这个构造函数在切片情况下被调用。然后,您至少可以获得一个运行时断言,并且您可能可以通过巧妙地使用模板获得编译时断言(例如:以在该构造函数中生成编译时断言的方式实例化模板)。在调用显式函数时,也可能有特定于编译器的方法来获取编译时警告或错误;例如,您可以在VisualStudio中的“切片构造函数”中使用“\uu declspec(弃用)”来获取编译时警告,至少在函数调用情况下是这样

因此,在您的示例中,代码如下所示(对于Visual Studio):

这在我的测试中有效(编译时警告)。请注意,它不能解决复制情况,但类似构造的赋值运算符应该在这里完成这项工作


希望这有帮助。:)

如果可以修改基类,可以执行以下操作:

class Base
{
public:
// not implemented will cause a link error
    Base(const Derived &d);
    const Base &operator=(const Derived &rhs);
};

取决于您的编译器,该编译器应该为您提供翻译单元,可能还有发生切片的函数。

解决此问题的最佳方法通常是遵循Scott Meyer的建议(请参见有效的C++)只在继承树的叶节点上有具体的类,并通过至少有一个纯虚函数(析构函数,如果没有其他函数的话)来确保非叶类是抽象的

令人惊讶的是,这种方法在其他方面也有助于澄清设计。在任何情况下,隔离公共抽象接口的工作通常都是值得的设计工作

编辑 虽然我最初没有明确说明这一点,但我的答案来自这样一个事实,即不可能在编译时准确地警告对象切片,因此,如果启用编译时断言或编译器警告,可能会导致错误的安全感。如果您需要了解对象切片的实例并需要更正它们,那么这意味着您有更改遗留代码的愿望和能力。如果是这种情况,那么我认为您应该认真考虑重构类层次结构,以使代码更加健壮。 我的理由是这样的

考虑一些库代码,这些代码定义了一个类Concrete1,并在该函数的推断面中使用它

void do_something( const Concrete1& c );
void do_something_else( const Concrete1& c );
传递类型be引用是为了提高效率,一般来说,这是一个好主意。如果库认为Concrete1是一种值类型,则实现可能会决定复制输入参数

void do_something( const Concrete1& c )
{
    // ...
    some_storage.push_back( c );
    // ...
}
如果传递的引用的对象类型确实是
Concrete1
,而不是其他派生类型,则此代码可以,不执行切片。关于此
push_back
函数调用的一般警告可能只会产生误报,很可能没有帮助

考虑从
Concrete1
派生出
Concrete2
并将其传递到另一个函数的一些客户端代码

void do_something( const Concrete1& c );
void do_something_else( const Concrete1& c );
由于该参数是通过引用获取的,因此此处未对要检查的参数进行切片,因此此处警告切片是不正确的,因为可能没有发生切片。将派生类型传递给使用引用或指针的函数是利用多态类型的一种常见且有用的方法,因此警告或禁止这样做似乎会适得其反

那么错误在哪里呢?“错误”是传递一个引用,该引用是从一个类派生的,然后被调用函数将其视为一个值类型

void do_something( const Concrete1& c );
void do_something_else( const Concrete1& c );
一般来说,没有办法针对对象切片生成一致有用的编译时警告,这就是为什么在可能的情况下,最好的防御措施是通过设计消除问题。

作为
class Base{
  public:
    Base() {}
    explicit Base(const Base &) {}
};

class Derived: public Base {};

void Func(Base)
{

}

//void Func(Derived)
//{
//
//}

//main
int main() {
  Func(Derived());
}