C ++ каскадный оператор [] к списку параметров operator ()? - PullRequest
2 голосов
/ 25 января 2012

У меня есть класс с operator() вот так:

struct S
{
    int operator()(int a, int b, int c, int d);
};

Пример использования:

S s;
int i = s(1, 2, 3, 4);

Мне нужно, чтобы мои пользователи могли использовать альтернативный синтаксис:

int i = s[1][2][3][4]; // equivalent to calling s(1, 2, 3, 4)

Я знаю, что мне нужно добавить S::operator[](int a) и что ему нужно вернуть вспомогательный объект. Но помимо этого все становится немного сложнее, и у меня возникает ощущение, что я заново изобретаю колесо, поскольку другие библиотеки (например, многомерные массивы), вероятно, уже предлагают подобный интерфейс.

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

Редактировать: в идеале я хотел бы добиться этого без каких-либо потерь времени выполнения на современном оптимизирующем компиляторе.

Ответы [ 4 ]

4 голосов
/ 25 января 2012

Вот и мы!

Прежде всего, код немного запутан - мне нужно накапливать значения аргументов по ходу работы, и единственный способ, которым я мог придумать (по крайней мере, в C ++ 03), - передать непосредственные индексы, установленные вокруг как массивы.

Я проверил это на G ++ 4.5.1 (Windows / MinGW) и подтверждаю, что на -O3 вызов:

s[1][2][3][4];

дает тот же код ассемблера, что и:

s(1,2,3,4);

Так что - никаких затрат времени выполнения, если ваш компилятор умен с оптимизацией. Хорошая работа, команда GCC!

Вот код:

#include <iostream>

template<typename T, unsigned N, unsigned Count>
struct PartialResult
{
    static const int IndicesRemembered = Count-1-N;
    T& t;
    int args[IndicesRemembered];
    PartialResult(T& t, int arg, const int* rest) : t(t) {
        for (int i=0; i<IndicesRemembered-1; ++i) {
            args[i] = rest[i];
        }
        if (IndicesRemembered>0) args[IndicesRemembered-1] = arg;
    }
    PartialResult<T, N-1, Count> operator[](int k) {
        return PartialResult<T, N-1, Count>(t, k, args);
    }
};

template<typename T, unsigned Count>
struct PartialResult<T, 0, Count>
{
    static const int IndicesRemembered = Count-1;
    T& t;
    int args[IndicesRemembered];
    PartialResult(T& t, int arg, const int* rest) : t(t) {
        for (int i=0; i<IndicesRemembered-1; ++i) {
            args[i] = rest[i];
        }
        if (IndicesRemembered>0) args[IndicesRemembered-1] = arg;
    }
    void operator[](int k) {
        int args2[Count];
        for (int i=0; i<Count-1; ++i) {
            args2[i] = args[i];
        }
        args2[Count-1] = k;
        t(args2);
    }
};

template<typename T, unsigned Count>
struct InitialPartialResult : public PartialResult<T, Count-2, Count> {
    InitialPartialResult(T& t, int arg)
        : PartialResult<T, Count-2, Count>(t, arg, 0) {}
};

struct C {

    void operator()(const int (&args)[4]) {
        return operator()(args[0], args[1], args[2], args[3]);
    }
    void operator()(int a, int b, int c, int d) {
       std::cout << a << " " << b << " " << c << " " << d << std::endl;
    }
    InitialPartialResult<C, 4> operator[](int m) {
        return InitialPartialResult<C, 4>(*this, m);
    }

};

А если серьезно, пожалуйста, не используйте это и просто придерживайтесь operator(). :) Ура!

1 голос
/ 25 января 2012

Это попытка подхода bind.Я сомневаюсь, что он особенно эффективен, и в нем есть некоторые неприятные моменты, но я выкладываю его на случай, если кто-нибудь знает, как это исправить.Пожалуйста, отредактируйте:

template <int N>
struct Helper {
    function_type<N>::type f;
    explicit Helper(function_type<N>::type f) : f(f) {}
    Helper<N-1> operator[](int p) {
        return Helper<N-1>(bound<N-1>(f,p));
    }
};

template<>
struct Helper<0> {
    function_type<0>::type f;
    explicit Helper(function_type<0>::type f) : f(f) {}
    operator int() {
        return f();
    }
};

Helper<3> S::operator[](int p) {
    return Helper<3>(std::bind(s, _1, _2, _3));
}

, где s - это выражение, которое возвращает operator(), привязанное к this.Что-то вроде std::bind(std::mem_fun(S::operator(), this, _1, _2, _3, _4)).Хотя я не могу вспомнить, может ли std::bind уже обрабатывать функции-члены, mem_fun может не понадобиться.

function_type<N>::type равно std::function<int, [int, ... n times]>, а bound<N> равно function_type<N>::type bound(function_type<N+1>::type f, int p) { return std::bind(f, p, _1, _2, ... _N); }Я не сразу уверен, как определить их рекурсивно, но вы можете просто перечислить их до некоторого предела.

1 голос
/ 25 января 2012

Я бы вообще избежал этого и предложил бы только operator(), но если вы действительно хотите сделать это, идея состоит в том, что operator[] вашего типа вернет объект вспомогательного типа, который содержит и ссылку на ваш объект и значение, которое было передано. Этот вспомогательный класс будет реализовывать operator[], снова сохраняя ссылку на исходный объект и аргументы обоих вызовов []. Это должно быть сделано для всех, кроме последнего уровня (т.е. достаточное количество помощников). На последнем уровне operator[] примет аргумент вместе со всеми ранее сохраненными значениями и вызовет operator() со всеми ранее сохраненными значениями плюс текущее значение.

Обычный способ сформулировать это - сказать, что каждый промежуточный тип связывает один из аргументов вызова с operator(), а последний выполняет вызов со всеми связанными аргументами.

В зависимости от того, хотите ли вы поддерживать большее или меньшее количество измерений массивов, вам может понадобиться / нужно еще больше усложнить это, чтобы сделать его универсальным. В целом, это не стоит усилий, и просто предложение operator() обычно является решением. Помните, что лучше делать вещи максимально простыми: меньше усилий писать и гораздо меньше усилий для обслуживания.

0 голосов
/ 25 января 2012

Вот реализация Fusion, которая поддерживает произвольные параметры и возвращаемые типы. Престижность любому, кто может заставить это работать (пожалуйста, дайте мне знать, если вы делаете)!

template <class Derived, class ReturnValue, class Sequence>
struct Bracketeer
{
    typedef ReturnValue result_type;
    typedef boost::fusion::result_of::size<Sequence> Size;

    struct RvBase
    {
        Sequence sequence;
        Derived *derived;
    };

    template <int n>
    struct Rv : RvBase
    {
        Rv(Derived *d) { this->derived = d; }
        Rv(RvBase *p) : RvBase(*p) { }
        Rv<n-1> operator[](typename boost::fusion::result_of::at_c<Sequence const, n-1>::type v)
        {
            boost::fusion::at_c<Size::value - 1 - n>(sequence) = v;
            return Rv<n-1>(this);
        }
    };

    template <>
    struct Rv<0> : RvBase
    {
        Rv(Derived *d) { this->derived = d; }
        Rv(RvBase *p) : RvBase(*p) { }
        ReturnValue operator[](typename boost::fusion::result_of::at_c<Sequence, Size::value - 1>::type v)
        {
            boost::fusion::at_c<Size::value - 1>(sequence) = v;
            return invoke(*derived, sequence);
        }
    };

    Rv<Size::value - 1> operator[](typename boost::fusion::result_of::at_c<Sequence, 0>::type v)
    {
        Rv<Size::value> rv(static_cast<Derived*>(this));
        return rv[v];
    }
};

struct S
    :
    Bracketeer<S, int, boost::fusion::vector<int, int, int, int> >
{
    int operator()(int a, int b, int c, int d);
};
...