How to implement a compile-time check that a downcast is valid in a CRTP?

做~自己de王妃 提交于 2020-01-02 02:33:10

问题


I have a plain old CRPT (please don't get distracted by access restrictions - the question is not about them):

 template<class Derived>
 class Base {
     void MethodToOverride()
     {
        // generic stuff here
     }
     void ProblematicMethod()
     {
         static_cast<Derived*>(this)->MethodToOverride();
     } 
 };

that is as usual intended to be used like this:

 class ConcreteDerived : public Base<ConcreteDerived> {
     void MethodToOverride()
     {
        //custom stuff here, then maybe
        Base::MethodToOverride();
     }
 };

Now that static_cast bothers me. I need a downcast (not an upcast), so I have to use an explicit cast. In all reasonable cases the cast will be valid since the current object is indeed of the derived class.

But what if I somehow change the hierarchy and the cast now becomes invalid?

May I somehow enforce a compile-time check that an explicit downcast is valid in this case?


回答1:


At compile-time you can only check the static types, and that's what static_cast already does.

Given a Base*, it is only, and can only be, known at run-time what its dynamic type is, that is, whether it actually points to a ConcreteDerived or something else. So if you want to check this, it has to be done at runtime (for example by using dynamic_cast)




回答2:


For extra safety, you could add a protected constructor to Base, to make sure that something is derived from it. Then the only problem would be for the really stupid:

class ConcreteDerived : public Base<SomeOtherClass>

but that should be caught by the first code review or test case.




回答3:


To expand on what @Bo Persson said, you can do a compile time check in said constructor using for example Boost.TypeTraits or C++0x/11 <type_traits>:

#include <type_traits>

template<class Derived>
struct Base{
  typedef Base<Derived> MyType;

  Base(){
    typedef char ERROR_You_screwed_up[ std::is_base_of<MyType,Derived>::value ? 1 : -1 ];
  }
};

class ConcreteDerived : public Base<int>{
};

int main(){
  ConcreteDerived cd;
}

Full example on Ideone.




回答4:


It seems that there exists a way to check CRPT correctness at compile time.

By making Base abstract (adding some pure virtual method to Base) we guarantee that any Base instance is a part of some derived instance.

By making all Base constructors private we can prevent undesirable inheritance from Base.

By declaring Derived as friend of Base we allow the only inheritance expected by CRPT.

After this, CRPT downcast should be correct (since something is inherited from base and this "something" may be only Derived, not some other class)

Perhaps for practical purpose the first step (making Base abstract) is redundant since successful static_cast guaranties that Derived is somewhere in the Base hierarchy. This allows only an exotic error if Derived is iherited from Base <Derived> (as CRPT expects) but at the same time Derived creates another instance of Base <derived> (without inheritance) somewhere in Derived code (it can, because it is a friend). However, I doubt that someone may accidentally write such exotic code.




回答5:


When you do something like below:

struct ConcreteDerived : public Base<Other>  // Other was not inteded

You can create objects of the class (derived or base). But if you try calling the function, it gives compilation error related to static_cast only. IMHO it will satisfy all practical scenarios.

If I correctly understood the question, then I feel the answer is in your question itself. :)



来源:https://stackoverflow.com/questions/5907731/how-to-implement-a-compile-time-check-that-a-downcast-is-valid-in-a-crtp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!