The force arising from the class is practically

We have a special structure for interfaces in our project, and part of the requirements is that the classes representing the interface can only be used as virtual base classes, and not as virtual ones. Is there a way to provide this in code? That is, create a compilation error if the class is derived from a non-virtual one.

I have access to C ++ 11 implemented by VS 2010: this means that static_assert , enable_if and <type_traits> .

+8
c ++ c ++ 11 virtual-inheritance typetraits static-assert
source share
3 answers

IMO, there is no clean and platform independent solution for this problem.

The best way is to manually go over and change each inheritance to virtual inheritance.
To do this, defining the derived classes of your interface (e.g. class Base ) is easy (!). Following are the following steps:

  • Make class Base as final (C ++ 11); those. class Base final { ...
  • Compile the code, it will generate a compiler error for all its derived classes
  • Go through and test each derived class and make inheritance as virtual
  • Remove the final keyword and compile the code successfully

This process (unfortunately) should be performed periodically, whenever you want to do such a health check.

+3
source share

This can be checked at compile time. The key is if we have a diamond pattern:

diamond

You can clearly distinguish D& from A& . However, if the inheritance is not virtual:

not diamond

casting would be mixed. So try to make a diamond!

 template <typename Base, typename Derived> class make_diamond { struct D2 : virtual Base { }; // this one MUST be virtual // otherwise we'd NEVER have a diamond public: struct type : Derived, D2 { }; }; 

At this point, this is just another void_t like type trait:

 template <typename Base, typename Derived, typename = void> struct is_virtual_base_of : std::false_type { }; template <typename Base, typename Derived> struct is_virtual_base_of<Base, Derived, void_t< decltype(static_cast<Base&>( std::declval<typename make_diamond<Base, Derived>::type&>())) >> : std::true_type { }; 

If the roll is unambiguous, the expression in partial specialization will be valid and this specialization will be preferable. If the listing is ambiguous, we will have a replacement failure and will eventually be the main one. Note that Base should not actually have any virtual member functions here:

 struct A { }; struct B : public A { }; struct C : virtual A { }; std::cout << is_virtual_base_of<A, B>::value << std::endl; // 0 std::cout << is_virtual_base_of<A, C>::value << std::endl; // 1 

And if it has some pure virtual member functions, we don’t need to redefine them, since we never build an object.

 struct A2 { virtual void foo() = 0; }; struct B2 : public A2 { void foo() override { } }; struct C2 : virtual A2 { void foo() override { } }; std::cout << is_virtual_base_of<A2, B2>::value << std::endl; // 0 std::cout << is_virtual_base_of<A2, C2>::value << std::endl; // 1 

Of course, if your class is marked final , this will not work. But then, if it were final , it would not matter what inheritance he had.

+3
source share

An interesting problem. You may be able to get closer to what you want by hiding the interface class and exposing the specific class that inherits the interface. This obviously entails some workarounds and awkwardness, but it can be tailored to your needs. Here is an example:

 #include <iostream> using namespace std; class Hide { struct VInterface { void foo() const { cout << "VInterface::foo()\n"; } VInterface const &as_interface() const { return *this; } protected: virtual ~VInterface() { } }; public: struct VBase : virtual VInterface { }; }; typedef Hide::VBase VBase; struct VDiamond1 : VBase { }; struct VDiamond2 : VBase { }; struct VConcrete : VDiamond1, VDiamond2 { }; int main() { VConcrete vc; auto const &vi = vc.as_interface(); vi.foo(); } 

It may be possible to recover the name with decltype() and as_interface() , which can be used for inheritance, but the ones I tried led to compiler errors protected by the destructor, so I expect that if possible, this is at least It is relatively difficult and may be sufficient for your needs.

+1
source share

All Articles