==== Assignment #1 ==== It depends on how you see it. - If we cannot change the interface (i.e. how we do the lookup: Lookup::value) then it is impossible, because the interface is through template parameters which are always evaluated during compile-time. During this constraint we can however make it so that the actual lookup is done during runtime by storing std::type_index in a std::map (or similar). This would be done something like this: template std::pair make_entry(Pair) { return { typeid(T), N }; }; template struct Lookup_Table; template struct Lookup_Table> { static std::map table { make_entry(Entries)... }; }; template struct Lookup { static int const value = Lookup_Table::table[typeid(T)]; }; But honestly, this seems like really bad idea since it is both more complicated and also has quite a steep runtime cost. If we are allowed to change the interface, then we can make it alot easier, based upon the idea presented above: std::map table { { typeid(int), 3 }, { typeid(double), -4 }, { typeid(float), 12 } }; And now we can just look it up with operator[]: table[typeid(int)] == 3 ==== Assignment #2 ==== It is necessary, because there are containers which have both operator+= and push_back, meaning we would have overlap between two cases. An example of such a type is std::string (more specifically std::basic_string). But it doesn't end there because every container that has push_back also have insert so even if we separate the operator+= and push_back case, we will still have overlap between push_back and insert. Therefore we have three functions with their own priorities, meaning it is not enough to add one overload parameter: we need to have two. So the priority would look like this: operator+= highest priority, so we add: int, int push_back second highest priority, so we add: int, float insert has lowest priority, so we add: float, float