C++11 "late binding" of template arguments
c++, c++11, templates
Solution
Maybe make your own comparator trait.
// Comparator trait primary template
template <typename T> stuct MyComparator
{
typedef typename T::key_compare type;
};
// Comparator trait non-standard usage example
template <typename U, typename V, int N>
struct MyComparator<WeirdContainer<U, V, N>>
{
typedef std::greater<V> type;
};
template <typename T, typename Cont = std::set<T>>
struct MyAdaptor
{
typedef typename MyComparator<Cont>::type comparator_type;
typedef T value_type;
// ...
};
I've renamed your "Containor" to "MyAdaptor", since this sort of construction is usually called an "adaptor" class.
Usage:
MyAdaptor<int> a; // uses std::set<int> and std::less<int>
MyAdaptor<double, WeirdContainer<bool, double, 27>> b;
Update: In light of the discussion, you could even remove the outer type argument entirely:
template <typename Cont> struct MyBetterAdaptor
{
typedef MyAdaptorTraits<Cont>::value_type value_type;
typedef MyAdaptorTraits<Cont>::pred_type pred_type;
// ...
};
To be used like this:
MyBetterAdaptor<std::set<int>> c; // value type "int", predicate "std::less<int>"
Writing the `MyAdaptorTraits` template is left as an exercise.
Problem
Please don't get my "late binding" wrong, I don't mean usual late binding at runtime, I mean something else and cannot find a better word for it: Consider I am working on a container (or similar) data structure `Containor` for some value type `V` that needs to compare these values with a comparator, so my first template looks like this ``` template<typename Val, typename Comp = std::less<Val>> struct Containor{}; ``` Now, my `Containor` structure makes use of another container internally. Which container is to be used should be configurable by template arguments as well, lets say the default is `std::set`. So my next version of `Containor` looks like this: ``` template<typename Val, typename Comp = std::less<Val>, typename Cont = std::set<Val,Comp>> struct Containor{}; ``` and here is where the code begins smelling IMHO. As long as the user is satisfied with the default implementation of the inner container, everything is fine. However, suppose he wants to use the new google btree set implementation `btree::btree_set` instead of `std::set`. Then he has to instanciate the template like this: ``` typedef Containor<int,std::less<int>,btree::btree_set<int,std::less<int>> MyContainor; ^^^^^^^^^^^^^^^^^^^ ``` I have underlined the part where my problem lies. The CLIENT CODE has to instanciate the btree_set with the right parameters. This honestly sucks, because the `Containor` class always needs a set of exactly the same type and comparator as its own first two template arguments. The client can - by accident - insert other types here! In addition, the client has the burden of choosing the right parameters. This might be easy in this case, but it hard if the inner container must for example be a set of pairs of the value type and some other type. Then the client has an even harder time getting the type parameters of the inner set correct. So what I want is a way in which the client code only hands in the raw template and the `Containor` internally instanciates it with the correct arguments, i.e. something like that: ``` template<typename Val, typename Comp = std::less<Val>, typename Cont = std::set > struct Containor{ typedef Cont<Val,Comp> innerSet; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ container instanciates the inner containor }; typedef Containor<int,std::less<int>,btree::btree_set> MyContainor; // ^^^^^^^^^^^^^^^^ // client only hands in raw template ``` Of course, this is no valid C++! So I thought about ways to solve this problem. The only solution I could think of was writing "binder classes" for all data structures I want to use, like this: ``` struct btree_set_binder{ template<typename V, typename C = std::less<V>> struct bind{ typedef btree::btree_set<V,C> type; } }; ``` Now I can define my `Containor` with a set binder ``` template<typename Val, typename Comp = std::less<Val>, typename ContBinder = btree_set_binder > struct Containor{ typedef btree_set_binder::bind<Val,Comp>::type innerSet; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ works like a charm }; ``` Now, the user must only supply the desired binder class and the `Containor` will instanciate it with the right arguments. So these binder classes would be okay for me, but it is quite a hassle writing binder classes for all containers. So is there a better or easier way to bind template arguments "late" in C++11, i.e., inside another template that retrieves the raw template as parameter.