2
\$\begingroup\$

This is the final version of my ordered-by-insertion map in C++. What do you think and how can I make it even better? I'm open to all kind of suggestions! I intend to make a similar class, this time, a ordered-by-insertion set!

#ifndef CORE_TABLE_H
#define CORE_TABLE_H

template <typename K, typename V>
class core_table {
    using value = std::pair<K, V>;
    using storage = std::list<value>;
    using iterator = typename storage::iterator;

    struct compare {
        using is_transparent = void;

        bool operator ()(iterator const x, iterator const y) const {
            return x->first < y->first;
        }

        bool operator ()(K const & k, iterator const y) const {
            return k < y->first;
        }

        bool operator ()(iterator const x, K const & k) const {
            return x->first < k;
        }
    };

    storage list;
    std::set<iterator, compare> data;

public:
    core_table() = default;

    core_table(core_table const & ct) {
        for (auto const & e : ct.list) {
            push(e.first, e.second);
        }
    }

    core_table(std::initializer_list<value> const il) {
        for (auto const & e : il) {
            push(e.first, e.second);
        }
    }

    core_table & unshift(K const & k, V const & v) {
        auto const it = data.find(k);
        if (it != data.end()) {
            (*it)->second = v;
            return *this;
        }
        data.emplace(list.emplace(list.begin(), k, v));
        return *this;
    }

    core_table & push(K const & k, V const & v) {
        auto const it = data.find(k);
        if (it != data.end()) {
            (*it)->second = v;
            return *this;
        }
        data.emplace(list.emplace(list.end(), k, v));
        return *this;
    }

    bool shift() {
        return remove(list.front().first);
    }

    bool pop() {
        return remove(list.back().first);
    }

    bool remove(K const & k) {
        auto const it = data.find(k);
        if (it != data.end()) {
            list.erase(*it);
            data.erase(it);
            return true;
        }
        return false;
    }

    core_table & clear() {
        list.clear();
        data.clear();
        return *this;
    }

    core_table & swap(core_table & ct) {
        list.swap(ct.list);
        data.swap(ct.data);
        return *this;
    }

    bool is_empty() const {
        return list.empty();
    }

    bool contains(K const & k) const {
        return data.find(k) != data.end();
    }

    size_t length() const {
        return list.size();
    }

    iterator begin() {
        return list.begin();
    }

    iterator end() {
        return list.end();
    }

    core_table head() const {
        if (is_empty()) {
            return {};
        }
        return {{list.front().first, list.front().second}};
    }

    core_table last() const {
        if (is_empty()) {
            return {};
        }
        return {{list.back().first, list.back().second}};
    }

    core_table lead() const {
        if (is_empty()) {
            return {};
        }
        core_table output {*this};
        output.pop();
        return output;
    }

    core_table tail() const {
        if (is_empty()) {
            return {};
        }
        core_table output {*this};
        output.shift();
        return output;
    }

    core_table edge() const {
        if (length() < 3) {
            return {*this};
        }
        return {
            {list.front().first, list.front().second},
            {list.back().first, list.back().second}
        };
    }

    core_table bulk() const {
        if (length() < 3) {
            return {};
        }
        core_table output {*this};
        output.shift();
        output.pop();
        return output;
    }

    std::vector<K> keys() const {
        std::vector<K> output;
        output.reserve(length());
        for (auto const & e : list) {
            output.emplace_back(e.first);
        }
        return output;
    }

    std::vector<V> values() const {
        std::vector<V> output;
        output.reserve(length());
        for (auto const & e : list) {
            output.emplace_back(e.second);
        }
        return output;
    }

    V & operator [](K const & k) {
        return (*data.find(k))->second;
    }

    V const & operator [](K const & k) const {
        return (*data.find(k))->second;
    }

    core_table & operator =(std::initializer_list<value> const il) {
        return swap(core_table {il});
    }

    explicit operator bool() const {
        return true;
    }

    bool operator ==(core_table const & ct) const {
        return list == ct.list;
    }

    bool operator !=(core_table const & ct) const {
        return !(*this == ct);
    }

    bool operator <(core_table const & ct) const {
        return list < ct.list;
    }

    bool operator >(core_table const & ct) const {
        return ct < *this;
    }

    bool operator <=(core_table const & ct) const {
        return !(*this > ct);
    }

    bool operator >=(core_table const & ct) const {
        return !(*this < ct);
    }
};

#endif
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$
  • You have written a copy constructor, so I should also be seeing an assignment operator. The default one will copy iterators over—not what I think you want!

    Also, you haven't defined the move operators for your class.

  • Although you have a swap method, you usually want to also have a free-standing version of the swap. This is useful when using templated functions, as most people will use the free-standing version of swap(). Just make it delegate to your existing member function:

    friend void swap(core_table& lhs, core_table& rhs) {
        lhs.swap(rhs);
    }
    

    Note that the swap operator is usually marked as noexcept to indicate that it can't throw an exception.

  • You define a non-const version of begin() and end(), but usually there are a couple of other versions to handle getting const iterators.

    const_iterator begin()  const;
    const_iterator cbegin() const;
    const_iterator end()    const;
    const_iterator cend()   const;
    
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.