Any reason to prefer static_cast over a chain of implicit conversions?

Suppose I have a class that implements several interfaces

class CMyClass : public IInterface1, public IInterface2 { }; 

and in the member function of this class I need to get a void* pointer to one of these interfaces (a typical situation in IUnknown::QueryInterface() .

A typical solution is to use static_cast to achieve pointer adjustment:

 void* pointer = static_cast<IInterface2*>( this ); 

and in this case, it is safe if there is no known class inherited from CMyClass . But if such a class exists:

 class CDerivedClass : public CUnrelatedClass, public CMyClass {}; 

and I accidentally do

 void* pointer = static_cast<CDerivedClass*>( this ); 

and this is actually a pointer to an instance of CMyClass , the compiler will not catch me, and the program may encounter undefined behavior later - static_cast becomes unsafe.

The proposed solution is to use an implicit conversion:

 IInterface2* interfacePointer = this; void* pointer = interfacePointer; 

It seems that this will solve both problems - pointer adjustment and the risk of an unacceptable downgrade.

Are there any problems in the second solution? What could be the prerequisites for the first?

+4
source share
5 answers

You can use this template:

 template<class T, class U> T* up_cast(U* p) { return p; } 

using:

 struct B {}; struct C : B {}; int main() { C c; void* b = up_cast<B>(&c); } 

Note that '*' is implicit. If you prefer up_cast<B*> , adjust the template accordingly.

+3
source

The void * assignment is always unsafe. Whichever way you write it, you can ruin it — assuming that the user is trying to execute QI for interface1, none of the following will be a warning or error:

 Interface2* interfacePointer = this; void* pointer = interfacePointer; 

or

 void* pointer = static_cast<Interface2*>( this ); 

Given the tiny risk of accidentally using static_cast for actuation, in a file that most likely doesn't even have access to the definition of the derived class, I see a lot of extra effort for very little actual security.

+2
source

I see no reason not to use the latter solution, except that if someone else is reading your code, it will not immediately communicate why you are using such a confusing expression ("why is it not just using static_cast?!? "), so it would be better to comment on this or make the intention very clear.

+2
source

Your analysis looks to me. The reason not to use your implicit approach is not convincing:

  • a bit more verbose
  • leaves variables hanging around
  • static_cast <> is probably more common, so it is more likely to be obvious to other developers, look, etc.
  • in many cases, even declarations of derived classes will not be displayed before defining the functions of the base class, so there is no possibility for this type of error
+1
source

If you are afraid to do something by accident using static_cast , then I suggest you wrap the capture of the fill / interface pointer in some template function, for example. eg:

 template <typename Interface, typename SourceClass> void set_if_pointer (void * & p, SourceClass * c) { Interface * ifptr = c; p = ifptr; } 

Alternatively, use dynamic_cast and check the value of the NULL pointer.

 template <typename Interface, typename SourceClass> void set_if_pointer (void * & p, SourceClass * c) { p = dynamic_cast<Interface *>(c); } 
+1
source

Source: https://habr.com/ru/post/1339147/


All Articles