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

发布于 2024-12-28 21:05:20 字数 570 浏览 1 评论 0原文

我有一个带有 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) 并且它需要返回一个辅助对象。但除此之外,一切都变得有点复杂,我有一种感觉,我正在重新发明轮子,因为其他库(例如多维数组)可能已经提供了类似的接口。

理想情况下,我只需使用现有的库来实现此目标。如果做不到这一点,我怎样才能用最通用的代码来实现我的目标呢?

编辑:理想情况下,我希望在现代优化编译器上实现这一目标,而不会造成任何运行时损失。

I have a class with operator() like this:

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

Example usage:

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

I need my users to be able to use an alternate syntax:

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

I know I need to add S::operator[](int a) and that it needs to return a helper object. But beyond that it all gets a bit complex and I have a feeling that I am reinventing the wheel since other libraries (e.g. multidimensional arrays) probably already offer similar interface.

Ideally I'd just use an existing library to achieve this goal. Failing that, how can I achieve my goal with the most generic code?

Edit: ideally I'd like to achieve this without any runtime penalty on a modern optimizing compiler.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

浮生面具三千个 2025-01-04 21:05:20

开始了!

首先,代码有点混乱——我必须在执行过程中累积参数值,而我能想到的唯一方法(至少在 C++03 中)是将立即索引设置为数组。

我已经在 G++ 4.5.1 (Windows / MinGW) 上检查过这一点,并确认在 -O3 上调用:

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

产生相同的汇编代码as:

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()。 :) 干杯!

Here we go!

First of all, the code is kind of messy- I have to accumulate the argument values as we go, and the only way I could think of (at least in C++03) is to pass the immediate indices set around as arrays.

I have checked this on G++ 4.5.1 (Windows / MinGW) and I confirm that on -O3 the call:

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

yields the same assembler code as:

s(1,2,3,4);

So - no runtime overhead if your compiler is smart with optimisations. Good job, GCC team!

Here goes the code:

#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);
    }

};

And seriously, please, don't use this and just stick with operator(). :) Cheers!

笑梦风尘 2025-01-04 21:05:20

这是对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 是一个返回绑定到 thisoperator() 的表达式。类似于 std::bind(std::mem_fun(S::operator(), this, _1, _2, _3, _4)) 的内容。虽然我不记得 std::bind 是否已经可以处理成员函数,但可能不需要 mem_fun 。

function_type::typestd::function,并且 bound< /code> 是 function_type::typebound(function_type::type f, int p) { return std::bind(f, p, _1、_2、..._N); }。我不确定如何递归地定义它们,但您可以将它们列出到一定限度。

This is an attempt at the bind approach. I doubt that it's particularly efficient, and it has some nasty bits in it, but I post it in case anyone knows how to fix it. Please edit:

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));
}

where s is an expression that returns operator() bound to this. Something along the lines of std::bind(std::mem_fun(S::operator(), this, _1, _2, _3, _4)). Although I can't remember whether std::bind can already handle member functions, mem_fun might not be needed.

function_type<N>::type is std::function<int, [int, ... n times]>, and bound<N> is function_type<N>::type bound(function_type<N+1>::type f, int p) { return std::bind(f, p, _1, _2, ... _N); }. I'm not immediately sure how to define those recursively, but you could just list them up to some limit.

怀念你的温柔 2025-01-04 21:05:20

我会完全避免这种情况,只提供 operator(),但如果您真的想尝试一下,想法是您的类型的 operator[] 将返回一个对象一个帮助器类型,它保存对对象的引用和传入的值。该帮助器类将通过再次存储对原始对象的引用和两个调用的参数来实现operator[]到[]。除了最后一个级别(即相当数量的助手)之外,所有级别都必须执行此操作。在最后一个级别,operator[] 将获取其参数以及所有先前存储的值,并使用所有先前存储的值加上当前值调用 operator()

一种常见的表述方式是,每个中间类型绑定operator()调用的参数之一,最后一个使用所有绑定参数执行调用。

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

I would avoid this altogether and offer just operator(), but if you really want to give it a shot, the idea is that your type's operator[] would return an object of a helper type that holds both a reference to your object and the value that was passed in. That helper class will implement operator[] by again storing a reference to the original object and the arguments to both calls to []. This would have to be done for all but the last level (I.e. a fair amount of helpers). I the last level, operator[] will take its argument together with all previously stored values and call operator() with all of the previously stored values plus the current value.

A common way of phrasing this is saying that each intermetiate type binds one of the arguments of the call to operator(), with the last one executing the call with all bound arguments.

Depending on whether you want to support more or less number of dimensions of arrays you might want/need to complicate this even more to make it generic. In general it is not worth the effort and just offering operator() is usually the solution. Remember that it is better to keep things as simple as possible: less effort to write and much less effort to maintain.

留一抹残留的笑 2025-01-04 21:05:20

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

Here is a Fusion implementation that supports arbitrary parameter and return types. Kudos to anyone that can get this working (please let me know if you do)!

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);
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文