This is a possible solution. It will always choose the operator with the highest alignment in a given hierarchy:
#include <exception>
#include <iostream>
#include <cstdlib>
// provides operators for any alignment >= 4 bytes
template<int Alignment>
struct DeAllocator;
template<int Alignment>
struct DeAllocator : virtual DeAllocator<Alignment/2> {
void *operator new(size_t s) throw (std::bad_alloc) {
std::cerr << "alignment: " << Alignment << "\n";
return ::operator new(s);
}
void operator delete(void *p) {
::operator delete(p);
}
};
template<>
struct DeAllocator<2> { };
// ........... Test .............
// different classes needing different alignments
struct Align8 : virtual DeAllocator<8> { };
struct Align16 : Align8, virtual DeAllocator<16> { };
struct DontCare : Align16, virtual DeAllocator<4> { };
int main() {
delete new Align8; // alignment: 8
delete new Align16; // alignment: 16
delete new DontCare; // alignment: 16
}
It's based on the dominance rule: If there is an ambiguity in lookup, and the ambiguity is between names of a derived and a virtual base class, the name of the derived class is taken instead.
Questions were risen why DeAllocator<I>
inherits DeAllocator<I / 2>
. The answer is because in a given hierarchy, there may be different alignment requirements imposed by classes. Imagine that IBase
has no alignment requirements, A
has 8 byte requirement and B
has 16 byte requirement and inherits A
:
class IBAse { };
class A : IBase, Alignment<8> { };
class B : A, Alignment<16> { };
Alignment<16>
and Alignment<8>
both expose an operator new
. If you now say new B
, the compiler will look for operator new
in B
and will find two functions:
// op new
Alignment<8> IBase
^ /
\ /
\ /
// op new \ /
Alignment<16> A
\ /
\ /
\ /
B
B -> Alignment<16> -> operator new
B -> A -> Alignment<8> -> operator new
Thus, this would be ambiguous and we would fail to compile: Neither of these hide the other one. But if you now inherit Alignment<16>
virtually from Alignment<8>
and make A
and B
inherit them virtually, the operator new
in Alignment<8>
will be hidden:
// op new
Alignment<8> IBase
^ /
/ \ /
/ \ /
// op new / \ /
Alignment<16> A
\ /
\ /
\ /
B
This special hiding rule (also called dominance rule) however only works if all Alignment<8>
objects are the same. Thus we always inherit virtually: In that case, there is only one Alignment<8>
(or 16, ...) object existing in any given class hierarchy.