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.