0

I want to declare class which depends on 'mode' field works with different type of standard containers. How I can do that?!

class Container {
public:
    Container(int8_t initMode);
    void addPair(string name, int32_t number);
    int8_t mode;
private:
    std::_Container_base _container;
};

Container::Container(int8_t m) {
    mode = m >= 0 && m <= 2 ? m : 0;
    switch (mode) {
    case 0: 
        //_container should be Map;
        break;
    case 1:
        //_container should be Dictionary;
        break;
    case 2:
        //_container should be HashMap;
        break;
    }
}
  • 2
    I really don't think you actually want to do this. Consider making use of [`std::variant`](https://en.cppreference.com/w/cpp/utility/variant) if you want a safer option than a runtime `mode` and a `union`. – Patrick Roberts Mar 06 '21 at 22:45
  • 1
    There is no such thing as `std::_Container_base` in the standard, and any use of this identifier makes your program behaviour undefined. – n. m. could be an AI Mar 06 '21 at 23:07
  • 1
    You are meant to pretend that std::_Container_base doesnt exist. https://stackoverflow.com/questions/9668947/reserved-names-in-the-global-namespace – QuatCoder Mar 07 '21 at 09:25

2 Answers2

1

When you declare a variable of a certain type, the variable must be that type exactly. You can instead use polymorphism and make _container a std::_Container_base*, and then allocate it as whatever subclass of std::_Container_base you want via new.

I don't think std::_Container_base is portable, though, so you should consider using a union of the containers you want, then switch which one you use based on the mode.

Otto von Bisquick
  • 177
  • 1
  • 2
  • 17
0

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;
}
m88
  • 1,968
  • 6
  • 14