C ++ контейнер / массив / кортеж согласованный интерфейс доступа - PullRequest
4 голосов
/ 23 января 2010

Существует ли, возможно, расширенная, согласованная семантика доступа к элементам, которая работает в контейнерах? что-то вроде:

element_of(std_pair).get<1>();
element_of(boost_tuple).get<0>();
element_of(pod_array).get<2>();

В принципе я могу написать сам, но я бы не стал изобретать велосипед. Спасибо

Ответы [ 3 ]

2 голосов
/ 23 января 2010

Контейнеры имеют разные способы доступа к ним, потому что они изначально разные. В STL ближе всего находятся итераторы. Все стандартные контейнеры имеют итераторы, поэтому вы можете выполнять итерации по ним и использовать те же алгоритмы для них, используя эти итераторы. Однако то, что содержит каждый итератор, отличается в зависимости от контейнера (элемент должен быть просто, а у карт есть пары). И если вы рассматриваете пару как контейнер, она не будет соответствовать остальным, потому что у нее нет итераторов.

В большинстве случаев использование итераторов решает проблему. Однако, очевидно, что это не полностью решает проблему, и у STL нет решения для этого. Повысить может, но я не знаю ни одного.

Суть, однако, заключается в том, что контейнеры по своей сути различны и в значительной степени не предназначены для взаимозаменяемости. Используя стандартные итераторы, большинство контейнеров можно легко заменить друг на друга. Но обычно нет смысла менять один контейнер на другой, не изменяя часть кода вокруг него, потому что они действуют по-разному. Я полагаю, что Скотт Мейерс подчеркивает это в своей книге «Эффективный STL».

Если вы действительно пытаетесь сделать различные контейнеры взаимозаменяемыми, я бы посоветовал переосмыслить это и присмотреться к тому, что вы делаете. Скорее всего, это не лучшая идея. Теперь, это может быть хорошей идеей для вашего конкретного приложения - я, конечно, не могу сказать, ничего не зная об этом, и вы были бы лучшим судьей этого - но в общем случае, сделать контейнеры действительно взаимозаменяемыми является плохая идея. Итераторы позволяют повторно использовать многие алгоритмы для них, но даже там тип алгоритмов, которые вы можете использовать в конкретном контейнере, варьируется в зависимости от типа итераторов, которые этот контейнер использует (произвольный доступ, двунаправленный и т. Д.).

Итак, нет, я не знаю о существующем ранее решении для доступа к элементам контейнера, кроме итераторов, но, вообще говоря, я бы посоветовал не пытаться это делать. Контейнеры не являются действительно взаимозаменяемыми и не предназначены для этого.

1 голос
/ 24 января 2010

Я не знаю о такой вещи.

Скорее всего, вы могли бы просто реализовать бесплатную функцию get для интересующих вас типов. В Boost.Tuple она уже есть. std::pair есть в C ++ 0x. А в остальном не должно быть слишком сложно.

* 1006 например *

#include <iostream>
#include <utility>
#include <vector>
#include <boost/tuple/tuple.hpp>

namespace getter
{
    template <size_t Index, class Container>
    typename Container::reference get(Container& c)
    {
        return c[Index];
    }

    template <size_t Index, class Container>
    typename Container::const_reference get(const Container& c)
    {
        return c[Index];
    }

    template <size_t Index, class T>
    T& get(T *arr)
    {
        return arr[Index];
    }

    namespace detail {
        template <size_t Index, class T, class U>
        struct PairTypeByIndex;

        template <class T, class U>
        struct PairTypeByIndex<0u, T, U>
        {
            typedef T type;
            type& operator()(std::pair<T, U>& p) const { return p.first; }
            const type& operator()(const std::pair<T, U>& p) const { return p.first; }
        };

        template <class T, class U>
        struct PairTypeByIndex<1u, T, U>
        {
            typedef U type;
            type& operator()(std::pair<T, U>& p) const { return p.second; }
            const type& operator()(const std::pair<T, U>& p) const { return p.second; }
        };
    }

    template <size_t Index, class T, class U>
    typename detail::PairTypeByIndex<Index, T, U>::type& get(std::pair<T, U>& p)
    {
        return detail::PairTypeByIndex<Index, T, U>()(p);
    }

    template <size_t Index, class T, class U>
    const typename detail::PairTypeByIndex<Index, T, U>::type& get(const std::pair<T, U>& p)
    {
        return detail::PairTypeByIndex<Index, T, U>()(p);
    }

    using boost::get;
}

int main()
{
    boost::tuple<int, int> tuple(2, 3);
    std::cout << getter::get<0>(tuple) << '\n';
    std::vector<int> vec(10, 1); vec[2] = 100;
    std::cout << getter::get<2>(vec) << '\n';
    const int arr[] = {1, 2, 3, 4, 5};
    std::cout << getter::get<4>(arr) << '\n';
    std::pair<int, float> pair(41, 3.14);
    ++getter::get<0>(pair);
    const std::pair<int, float> pair_ref = pair;
    std::cout << getter::get<0>(pair_ref) << ' ' << getter::get<1>(pair_ref) << '\n';
}
1 голос
/ 23 января 2010

Мне неизвестны какие-либо универсальные средства доступа, которые бы работали во всех известных определениях контейнеров в C ++. Тем не менее, Boost.Range может использоваться как таковой в некоторой степени.

Для большей гибкости вам, вероятно, потребуется реализовать это самостоятельно. Возможно, что-то царапается по этому поводу:

struct container_accessor { ... }
template <typename Container>
container_accessor make_accessor(Container& c) { ... }

template <typename Container>
container_const_accessor make_accessor(Container const& c) { ... }

где и затем специализировать container_accessor для всех необходимых вам контейнеров.

...