You could use a variant, you wouldn't even need a mode
attribute since std::holds_alternative<MapType>(_container)
can give you that info.
template <class T>
class Container {
public:
private:
using Key = std::string;
std::variant<
std::map<Key,T>,
std::multimap<Key,T>,
std::unordered_map<Key,T>,
std::unordered_multimap<Key,T>> _container;
}
https://en.cppreference.com/w/cpp/utility/variant
More complete example:
#include <iostream>
#include <string>
#include <map>
#include <stdexcept>
#include <unordered_map>
#include <variant>
template <class T>
class Container {
public:
using Key = std::string;
template<class C> Container(const C& c) : _container(c) {}
template<class C> Container(C&& c) : _container(std::move(c)) {}
template<class C>
Container& operator=(const C& c) { _container = c; return *this; }
template<class C>
Container& operator=(C&& c) { _container = std::move(c); return *this; }
T getFirstElement(const Key& key) {
if (!hasKey(key)) throw std::runtime_error("No such key: " + key);
return std::visit([this, key](auto& cont){ return cont.find(key)->second; }, _container);
}
T getFirstElement(const Key& key, T or_value) noexcept {
if (!hasKey(key)) return or_value;
return getFirstElement(key);
}
bool hasKey(const Key& key) const {
return std::visit([this, key](auto& cont){ return cont.count(key); }, _container) > 0;
}
private:
std::variant<
std::map<Key,T>,
std::multimap<Key,T>,
std::unordered_map<Key,T>,
std::unordered_multimap<Key,T>> _container;
};
int main()
{
std::map<std::string, int> someMap {{"one", 1}, {"two", 2}};
std::unordered_map<std::string, int> someUmap {{"one", 11}, {"two", 22}};
Container<int> cont(someMap);
std::cout << cont.getFirstElement("two") << std::endl; // 2
cont = someUmap;
std::cout << cont.getFirstElement("two") << std::endl; // 22
return 0;
}