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

Bai*_*ang 17 c++ compiler-construction truncate slice

我想知道是否可以让编译器为代码发出警告/错误,如下所示:

注意:

1.是的,这是糟糕的编程风格,我们应该避免这种情况 - 但我们正在处理遗留代码,并希望编译器可以帮助我们识别这些情况.)

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

class Base{};
class Derived: public Base{};

void Func(Base)
{

}

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

//main
Func(Derived());
Run Code Online (Sandbox Code Playgroud)

在这里,如果我注释掉第二个函数,第一个函数将被调用 - 而编译器(VC++和Gcc)对此感觉很舒服.

它是C++标准吗?并且我可以问编译器(VC++)在遇到这样的代码时给我一个警告吗?

非常感谢!!!

编辑:

非常感谢你的帮助!

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

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

编辑

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

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=421579

-Baiyan

And*_*ian 16

如果你可以修改基类,你可以做类似的事情:

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

取决于您的编译器应该为您提供翻译单元,也可能是切片发生的功能.

  • 是的,它类似于 Nick 的解决方案,它很酷,但它假设我们已经知道哪个类存在此类问题 - 这是不切实际的,因为我们的代码库中有很多类。但是谢谢,它确实有助于识别已知类的对象切片。 (2认同)
  • 在 C++11 中,引入了“=delete”以将其转变为编译器错误。 (2认同)

j_r*_*ker 8

作为Andrew Khosravian答案的变体,我建议使用模板化复制构造函数和赋值运算符.这样,您不需要知道给定基类的所有派生类,以保护该基类不被切片:

class Base
{
private:   // To force a compile error for non-friends (thanks bk1e)
// Not implemented, so will cause a link error for friends
    template<typename T> Base(T const& d);
    template<typename T> Base const& operator=(T const& rhs);

public:
// You now need to provide a copy ctor and assignment operator for Base
    Base(Base const& d) { /* Initialise *this from d */ }
    Base const& operator=(Base const& rhs) { /* Copy d to *this */ }
};
Run Code Online (Sandbox Code Playgroud)

虽然这减少了所需的工作量,但是通过这种方法,您仍然需要弄乱每个基类以保护它.此外,如果有合法的转换会造成问题,BaseSomeOtherClass雇用的operator Base()成员SomeOtherClass.(在这种情况下,boost::disable_if<is_same<T, SomeOtherClass> >可以使用更复杂的解决方案.)在任何情况下,一旦确定了对象切片的所有实例,就应该删除此代码.

对于世界的编译器实现者:测试对象切片绝对是值得创建(可选)警告的东西!我想不出一个需要行为的实例,并且它在新手C++代码中很常见.

[编辑27/3/2015:]正如Matt McNab所指出的,你实际上并不需要像我上面那样明确地声明复制构造函数和赋值运算符,因为它们仍然会被编译器隐式声明.在2003 C++标准中,在12.8/2下的脚注106中明确提到了这一点:

因为模板构造函数永远不是复制构造函数,所以这种模板的存在不会抑制复制构造函数的隐式声明.模板构造函数与其他构造函数(包括复制构造函数)一起参与重载解析,并且如果模板构造函数提供比其他构造函数更好的匹配,则可以使用模板构造函数来复制对象.


Nic*_*ick 5

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

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

class Base { ...
    __declspec(deprecated) Base( const Derived& oOther )
    {
        // Static assert here if possible...
    }
...
Run Code Online (Sandbox Code Playgroud)

这在我的测试中有效(编译时警告)。请注意,它不能解决复制情况,但类似构造的赋值运算符应该可以解决这个问题。

希望这可以帮助。:)