C ++级联operator []到operator()参数列表?

时间:2012-01-25 12:13:52

标签: c++ operator-overloading metaprogramming syntactic-sugar

我有一个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 个答案:

答案 0 :(得分:4)

我们走了!

首先,代码有点混乱 - 我必须积累参数值,而我能想到的唯一方法(至少在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 :(得分:1)

我会完全避免这种情况而只提供operator(),但是如果你真的想试一试,那么你的类型operator[]会返回一个包含两者的辅助类型的对象。对象的引用和传入的值。该助手类将通过再次存储对原始对象的引用和对operator[]的两个调用的参数来实现[]。除了最后一级(即,相当数量的帮助者)之外,所有人都必须这样做。我是最后一个级别,operator[]将其参数与所有先前存储的值一起使用,并使用所有先前存储的值加上当前值调用operator()

一种常用的表达方法是说每个中间类型绑定调用operator()的一个参数,最后一个执行带有所有绑定参数的调用。< / p>

根据您是否要支持更多或更少数量的数组维度,您可能需要/需要使其复杂化以使其更通用。一般来说,这不值得付出努力,只提供operator()通常是解决方案。请记住,最好保持尽可能简单的事情:减少编写工作量,减少维护工作量。

答案 2 :(得分:1)

这是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>::typestd::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); }。我不是立即确定如何递归地定义它们,但你可以将它们列出一些限制。

答案 3 :(得分:0)

这是一个支持任意参数和返回类型的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);
};