For some of the algorithms that I am implementing it turned out to be useful storing ids in order to iterate on objects, while also avoiding having to think about reallocation and invalidation of pointers.

It turned out that being able to iterate on sets of ids is something which is quite useful in general (at least to me): iterating over subsets, filtering, etc. I have made a relatively simple iterable that took care of the details for me, and that I could also use with STL algorithms.

I'm trying to avoid having these classes become too bulky, so I haven't tried to add all iterator methods I could have; for example, the iterator implemented could be a random-access one, but it has nowhere near the amount of stuff it should have in order to be classified as one. At the same time I'm open to adding functionality if it obviously missing and doesn't have a high line count.

I'm looking for any kind of comments: naming, API, performance, etc. Thanks!

```
#include <utility>
#include <type_traits>

/**
 * @brief This class is an iterable construct on a list of ids on a given container.
 *
 * This class allows to iterate over a given subset of ids on the input
 * container as if they were laid out continuously in a single object.
 *
 * Both ids and items must be stored in containers accessible via
 * square-brakets and ids.
 *
 * By default the iterable will copy the input ids and own them. If this is not
 * desirable (maybe one wants to change the ids over time without being forced
 * to copy them multiple times), the class accepts a pointer to an ids
 * container, and it will automatically store a single reference to it, rather
 * than doing a copy.
 *
 * @tparam IdsContainer The type of the input ids in the constructor.
 * @tparam Container The type of the container to be iterated on.
 */
template <typename IdsContainer, typename Container>
class SubsetIterable {
    public:
        template <typename T>
        class SubsetIterator;

        using value_type     = typename Container::value_type;
        using iterator       = SubsetIterator<typename copy_const<value_type, Container>::type>;
        using const_iterator = SubsetIterator<const value_type>;

        /**
         * @brief The type used to contain the ids in the iterable.
         *
         * This is a constant copy of the input ids if we own them, and
         * otherwise a const reference if we don't (and thus they can change).
         */
        static constexpr bool OwnsIds = !std::is_pointer_v<IdsContainer>;
        using IdsStorage  = typename std::conditional<OwnsIds, 
                                         const IdsContainer, 
                                         const std::remove_pointer_t<IdsContainer> &
                                     >::type;

        /**
         * @brief Basic constructor for owning iterable.
         *
         * This constructor stores a copy of all the ids and a reference to the
         * container over which to iterate.
         *
         * This class and its iterators do *NOT* perform any bound checking on
         * the size of the container and the input ids, neither at construction
         * nor during operation.
         *
         * This class and its iterators *WILL* be invalidated if the item
         * container is destroyed.
         *
         * @param ids The ids to iterate over.
         * @param items The items container.
         */
        template <bool Tmp = OwnsIds, typename std::enable_if_t<Tmp, int> = 0>
        SubsetIterable(IdsContainer ids, Container & items) : ids_(std::move(ids)), items_(items) {}

        /**
         * @brief Basic constructor for non-owning iterable.
         *
         * This constructor stores the pointer to the ids and items over which
         * to iterate.
         *
         * This class and its iterators do *NOT* perform any bound checking on
         * the size of the container and the input ids, neither at construction
         * nor during operation.
         *
         * This class and its iterators *WILL* be invalidated if the ids
         * container or the item container are destroyed.
         *
         * If the ids change, all previously generated iterators are invalidated.
         *
         * @param ids The ids to iterate over.
         * @param items The items container.
         */
        template <bool Tmp = OwnsIds, typename std::enable_if_t<!Tmp, int> = 0>
        SubsetIterable(IdsContainer ids, Container & items) : ids_(*ids), items_(items) {}

        /**
         * @brief This function returns an iterator to the beginning of this filtered range.
         */
        iterator begin() { return ids_.size() ? iterator(this) : iterator(); }

        /**
         * @brief This function returns a const_iterator to the beginning of this filtered range.
         */
        const_iterator begin() const { return cbegin(); }

        /**
         * @brief This function returns a const_iterator to the beginning of this filtered range.
         */
        const_iterator cbegin() const { return ids_.size() ? const_iterator(this) : const_iterator(); }

        /**
         * @brief This function returns an iterator to the end of this filtered range.
         */
        iterator end() { return iterator(); };

        /**
         * @brief This function returns a const_iterator to the end of this filtered range.
         */
        const_iterator end() const { return cend(); }

        /**
         * @brief This function returns a const_iterator to the end of this filtered range.
         */
        const_iterator cend() const { return const_iterator(); }

        /**
         * @brief This function returns the size of the range covered.
         */
        size_t size() const { return ids_.size(); }

    private:
        friend iterator;
        friend const_iterator;

        // Const reference if non-owning, const value otherwise.
        IdsStorage ids_;
        Container & items_;
};

/**
 * @brief This class is a simple iterator to iterate over filtered values held in a SubsetIterable.
 */
template <typename IdsContainer, typename Container>
template <typename T>
class SubsetIterable<IdsContainer, Container>::SubsetIterator {
    private:
        // The type of the SubsetIterable that defined this instance of the class
        using Owner = typename copy_const<SubsetIterable<IdsContainer, Container>, T>::type;
    public:
        using value_type = T;

        /**
         * @brief Basic constructor for end iterators.
         */
        SubsetIterator() : currentId_(0), parent_(nullptr) {}

        /**
         * @brief Basic constructor for begin iterators.
         *
         * @param parent The parent iterable object holding ids and values.
         */
        SubsetIterator(Owner * parent) : currentId_(0), parent_(parent) {}

        value_type& operator*()  { return parent_->items_[parent_->ids_[currentId_]]; }
        value_type* operator->() { return &(operator*()); }

        /**
         * @brief This function returns the equivalent item id of this iterator in its container.
         */
        size_t toContainerId() const { return parent_->ids_[currentId_]; }

        void operator++() {
            ++currentId_;
            if ( currentId_ >= parent_->ids_.size() ) {
                currentId_ = 0;
                parent_ = nullptr;
            }
        }

        bool operator==(const SubsetIterator & other) {
            if ( parent_ == other.parent_ ) return currentId_ == other.currentId_;
            return false;
        }
        bool operator!=(const SubsetIterator & other) { return !(*this == other); }

    private:
        size_t currentId_;
        Owner * parent_;
};

```

Example usage:

```
int main() {
    std::vector<std::string> test{"abc", "cde", "lol", "lal", "foo", "baz"};
    std::vector<size_t> ids{0,3,4,5};

    SubsetIterable itt(ids, test);
    for (const auto & s : itt)
        std::cout << s << '\n';
    std::cout << '\n';

    SubsetIterable itt2(&ids, test);
    for (const auto & s : itt2)
        std::cout << s << '\n';
    std::cout << '\n';

    ids[0] = 1;
    for (const auto & s : itt2)
        std::cout << s << '\n';
    std::cout << '\n';
    return 0;
}

```