How to generate a compiler warning/error when obje

2019-03-12 11:53发布

I want to know if it is possible to let compiler issue a warning/error for code as following:

Note:

1. Yea, it is bad programming style and we should avoid such cases - but we are dealing with legacy code and hope compiler can help identify such cases for us.)

2. I prefer a compiler option (VC++) to disable or enable object slicing, if there is any.

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

void Func(Base)
{

}

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

//main
Func(Derived());

Here if I comment out the second function, the first function would be called - and the compiler (both VC++ and Gcc) feels comfortable with that.

Is it C++ standard? and can I ask compiler (VC++) to give me a warning when met such code?

Thanks so much!!!

Edit:

Thanks all so much for your help!

I can't find a compiler option to give a error/warning - I even posted this in MSDN forum for VC++ compiler consultant with no answer. So I am afraid neither gcc nor vc++ implemented this feature.

So add constructor which take derived classes as paramter would be the best solution for now.

Edit

I have submit a feedbak to MS and hope they will fix it soon:

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

-Baiyan

8条回答
来,给爷笑一个
2楼-- · 2019-03-12 12:16

Not really a solution to your immediate problem, but....

Most functions that take class/struct objects as parameters should declare the parameters to be of type "const X&" or "X&", unless they have a very good reason not to.

If you always do this, object slicing will never be a problem (references don't get sliced!).

查看更多
聊天终结者
3楼-- · 2019-03-12 12:23

If you can modify the base class you could do something like:

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

Depending on your compiler that should get you the translation unit, and maybe the function where the slicing is happening.

查看更多
趁早两清
4楼-- · 2019-03-12 12:29

The best way to combat this problem is usually to follow Scott Meyer's recommendation (see Effective C++) of only having concrete classes at the leaf nodes of your inheritance tree and ensuring that non-leaf classes are abstract by having at least one pure virtual function (the destructor, if nothing else).

It is surprising how often this approach helps clarify the design in other ways, as well. The effort of isolating a common abstract interface is usually a worthwhile design effort in any case.

Edit

Although I originally didn't make this clear, my answer comes from the fact that it is not possible to warn accurately about object slicing at compile time and for this reason it can lead to a false sense of security if you have a compile time assertion, or a compiler warning enabled. If you need to find out about instances of object slicing and need to correct them then it implies that you have the desire and ability to change the legacy code. If this is the case, then I believe that you should seriously consider refactoring the class hierarchy as a way of making the code more robust.

My reasoning is this.

Consider some library code that defines a class Concrete1 and uses it in the inferface to this function.

void do_something( const Concrete1& c );

Passing the type be reference is for efficiency and is, in general, a good idea. If the library considers Concrete1 to be a value type the implementation may decided to make a copy of the input parameter.

void do_something( const Concrete1& c )
{
    // ...
    some_storage.push_back( c );
    // ...
}

If the object type of the passed reference is, indeed, Concrete1 and not some other derived type then this code is fine, no slicing is performed. A general warning on this push_back function invocation might produce only false positives and would most likely be unhelpful.

Consider some client code that derives Concrete2 from Concrete1 and passes it into another function.

void do_something_else( const Concrete1& c );

Because the parameter is taken by reference no slicing occurs here on the parameter to check, so it would not be correct to warn here of slicing as it may be that no slicing occurs. Passing in a derived type to a function that takes a reference or pointer is a common and useful way to take advantage of polymorphic types so warning or disallowing this would seem counter-productive.

So where is there error? Well the 'mistake' is passing in a reference to something that is derived from a class that is then treated as though it is a value type by the called function.

There is, in general, no way to generate a consistently useful compile time warning against object slicing and this is why the best defence, where possible, is to eliminate the problem by design.

查看更多
男人必须洒脱
5楼-- · 2019-03-12 12:33

I modified your code slightly:

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

class Derived: public Base {};

void Func(Base)
{

}

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

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

The explicit keyword will make sure that the constructor will not be used as an implicit conversion operator - you have to call it explicitly when you want to use it.

查看更多
Ridiculous、
6楼-- · 2019-03-12 12:36

This is commonly called Object Slicing and is a well-known enough problem to have its own Wikipedia article (although it is only a short description of the problem).

I believe I have used a compiler that had a warning you could enable to detect and warn about this. However, I don't recall which one that was.

查看更多
叛逆
7楼-- · 2019-03-12 12:38

I would suggest adding a constructor to your base class which takes a const reference to the derived class explicitly (with a forward declaration). In my simple test app, this constructor gets called in the slicing case. You could then at least get a run-time assertion, and you could probably get a compile-time assertion with clever use of templates (eg: instantiate a template in a way which generates a compile-time assertion in that constructor). There may also be compiler-specific ways to get compile time warnings or errors when you call explicit functions; for example, you can use "__declspec(deprecated)" for the "slice constructor" in Visual Studio to get a compile-time warning, at least in the function-call case.

So in your example, the code would look like this (for Visual Studio):

class Base { ...
    __declspec(deprecated) Base( const Derived& oOther )
    {
        // Static assert here if possible...
    }
...

This works in my test (compile-time warning). Note that it doesn't solve the copy case, but a similarly-constructed assignment operator should do the trick there.

Hope this helps. :)

查看更多
登录 后发表回答