Dies kann zum Zeitpunkt der Kompilierung überprüft werden. Der Schlüssel ist, dass, wenn wir ein Diamantmuster haben:
Sie eindeutig D&
-A&
werfen können. Wenn jedoch die Vererbung ist nicht virtuell:
die Besetzung wäre mehrdeutig. Also lass uns versuchen, einen Diamanten zu machen!
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 { };
};
an welcher Stelle es ist nur ein weiteres void_t
-Stil Typ Merkmal:
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 { };
Wenn die Umwandlung eindeutig ist, wird der Ausdruck in der teilweisen Spezialisierung gültig und dass Spezialisierung bevorzugt. Wenn der Cast zweideutig ist, haben wir einen Substitutionsfehler und landen am Primary. Beachten Sie, dass Base
hier nicht eigentlich keine virtual
Member-Funktionen haben müssen:
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
Und wenn es keine rein virtuelle Member-Funktionen hat, wir haben sie nicht außer Kraft zu setzen, da wir eigentlich nie ein Objekt konstruieren .
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
Natürlich, wenn Ihre Klasse final
markiert ist, wird dies nicht funktionieren. Aber dann, wenn es final
wäre, wäre es egal, welche Art von Vererbung es ohnehin hatte.
Es gibt möglicherweise bessere Möglichkeiten, dies jetzt zu tun, aber in VS2010 gibt es ein Microsoft-spezifisches Schlüsselwort namens "__interface". Dies ist jedoch a) nicht portabel und b) Sie müssen vorsichtig sein, da diese Schnittstellen aus irgendeinem Grund keinen virtuellen Destruktor haben dürfen. – Excelcius
Das klingt eher nach einer technischen Spezifikation als nach einer funktionalen Anforderung. Warum brauchst du das? –
@Excelcius Wir entwickeln sowohl für Linux als auch für Windows; Ich habe VS2010 aufgelistet, weil gcc in C++ 11 Unterstützung voraus ist. – Angew