You have two main problems: 1) You have no specific comparison criteria (Hence the question, isn't?), and 2) You don't have any standard way to sort at compile-time.
For the first use std::type_info
as others suggested (Its currently used on maps via the std::type_index
wrapper) or define your own metafunction to specify the ordering criteria for different types. For the second, you could try to write your own template-metaprogramming based quicksort algorithm. Thats what I did for my personal metaprogramming library and works perfectly.
About the assumption "A self-balancing search tree should perform better than classic typelists" I really encourage you to do some profillings (Try templight) before saying that. Compile-time performance has nothing to do with classic runtime performance, depends heavily in the exact implementation of the template instantation system the compiler has.
For example, based on my own experience I'm pretty sure that my simple "O(n)" linear search could perform better than your self balanced tree. Why? Memoization. Compile-time performance is not only instantation depth. In fact memoization has a crucial role on this.
To give you a real example: Consider the implementation of quicksort (Pseudo meta code):
list sort( List l )
{
Int pivot = l[l.length/2];
Tuple(List,List) lists = reorder( l , pivot , l.length/2 );
return concat( sort( lists.left ) , sort( lists.right ) );
}
I hope that example is self-explanatory. Note the functional way it works, there are no side effects. I will be glad if some day metaprogramming in C++ has that syntax...
Thats the recursive case of quicksort. Since we are using typelists (Variadic typelists in my case), the first metainstruction, which computes the value of the pivot has O(n) complexity. Specifically requires a template instantation depth of N/2. The seconds step (Reordering) could be done in O(n), and concatting is O(1) (Remember that are C++11 variadic typelists).
Now consider an example of execution:
[1,2,3,4,5]
The first step calls the recursive case, so the trace is:
Int pivot = l[l.length/2];
traverses the list until 3. That means the instantations needed to perform the traversings [1]
, [1,2]
, [1,2,3]
are memoized.
During the reordering, more subtraversings (And combinations of subtraversing generated by element "swapping") are generated.
Recursive "calls" and concat.
Since such linear traversings performed to go to the middle of the list are memoized, they are instantiated only once along the whole sort execution. When I first encountered this using templight I was completely dumbfounded. The fact, looking at the instantations graph, is that only the first large traverses are instantiated, the little ones are just part of the large and since the large where memoized, the little are not instanced again.
So wow, the compiler is able of memoizing at least the half of that so slow linear traversings, right? But, what is the cost of such enormous memoization efforts?
What I'm trying to say with this answer is: When doing template meta-programming, forget everything about runtime performance, optimizations, costs, etc, and don't do assumptions. Measure. You are entering in a completely different league. I'm not completely sure what implementation (Your selft balancing trees vs simple linear traversing) is faster, because that depends on the compiler. My example was only to show how actually a compiler could break down completely your assumptions.
Side note: The first time I did that profilings I showed them to an algorithms teacher of my university, and he's still trying to figure out whats happening. In fact, he asked a question here about how to measure the complexity and performance of this monster: Best practices for measuring the run-time complexity of a piece of code