c – 仿函数如何维护/存储对象的状态

我是C noob学习仿函数.我有这个代码如下(注意 – 这不是我的功课,我已经过去了!).

它在控制台上打印0 1 2 3 4 5 6 7 8 9
我没看到它是如何保持这个对象的状态(n的值)如果函数是通过值而不是通过引用/指针调用的

编辑:
我想到了这里(例1),因为函数被Value调用,构造函数每次都将n初始化为零.因此它应该始终为零,然后它应该增加到1并返回1.如何打印0 1 2 3 4 5 6 7 8 9

例1]

class g
{
public:
    g():n(0){}
    int operator()() { return n++; }
    int n;
};

;

int main()
{
    int a[10];
    g v1;
    std::generate(a, a+10, g());//This passes a functor to generate 

    //EDIT - this will print 0 1 2 3 4 5 6 7 8 9**
    std::copy(a, a+10, std::ostream_iterator<int>(std::cout, " "));

    getchar();
    return 0;
}

因为我已经看到类似下面的代码使用仿函数中的引用变量来保留状态,here并使用以下概念开发了一个简单的代码:

例2]

class CountingFunctor
{
public:
    CountingFunctor() : _counter(0) {}
    int getCounter(void) const {return(_counter);}
    void operator () (Contained item) {if(item.getShouldBeCounted()) _counter++;}
private:
    int _counter;
};
#endif

//this class uses references to maintain state in the functor
class CountingFunctor
{
public:
    CountingFunctor(int &elem) : _counter(elem) {_counter=0;}
    int getCounter(void) const {return(_counter);}
    void operator () (Contained item) {if(item.getShouldBeCounted()) _counter++;}
private:
    int &_counter;
};

int main()
{
    vector<Contained> Container(10);
    Container[3].setShouldBeCounted(false);
    Container[9].setShouldBeCounted(false);
    int elem;
    CountingFunctor CountAllWhoShouldBe(elem);
    std::for_each(Container.begin(), Container.end(), CountAllWhoShouldBe);
    std::cout << CountAllWhoShouldBe.getCounter() << " items should be counted." << std::endl;

    getchar();
}

问题是

因此,仿函数自己维护对象的状态,即不需要任何参考变量,如示例2所示

或者示例1中的代码正常工作,因为std :: generate()通过引用/指针调用仿函数?

进一步阅读材料赞赏.

最佳答案 当你调用std :: generate时,它会获得自己的仿函数对象副本.一旦进入该函数,它只是重复调用它自己的对象的单个实例,因此状态保留在generate调用中,但不在generate和调用者之间.

所以,将代码更改为

g v1;
std::generate(a, a+10, v1);

之后v1.n仍为零.在内部生成它是在它的本地副本(比如v2)上运行,它确实增加了,但是无法告诉v1它.

现在,如果你想将v2的状态传达给v1,那就是当你需要在你的仿函数中使用引用时,所以v1和v2共享在调用中变异的状态.

我们可以扩展通话以更清楚地显示:

g v1;
std::generate(a, a+10, v1);
// -> generate(begin=a, end=a+10, v2=g(v1))
{
    while (begin != end)
        *begin = v2();
}
// v2 just went out of scope, and took the accumulated state with it!
// v1 in the caller's scope remains unchanged

现在显而易见的是,如果v1不是作为深度复制并在内部保持其状态的值对象,而是保持对共享状态的引用并且被浅层复制,那么v2将与v1共享相同的状态,并且该状态将通话结束后可以访问.

事实上,我们可以编写一个简单的包装器来自动执行此操作,因此您无需为每个仿函数手动执行此操作:

template <typename OriginalFunctor, typename RType>
class StatefulFunctor
{
    OriginalFunctor &fun;

public:
    StatefulFunctor() = delete;
    StatefulFunctor(OriginalFunctor &orig) : fun(orig) {}
    StatefulFunctor(StatefulFunctor const &other) : fun(other.fun) {}
    StatefulFunctor(StatefulFunctor &&other) : fun(other.fun) {}

    template <typename... Args>
    RType operator() (Args&&... args)
    {
        return fun(std::forward<Args>(args)...);
    }
};

template <typename RT, typename OF>
StatefulFunctor<OF, RT> stateful(OF &fun)
{
    return StatefulFunctor<OF, RT>(fun);
}

现在将原始代码更改为:

g v1;
std::generate(a, a+10, stateful<int>(v1));

意味着v1.i将在适当的位置更新.

正如Jerry Coffin指出的那样,即使在通话中保持状态也不能得到保证,因此即使您不需要为呼叫者保留状态,使用状态仿函数执行此类操作也是明智的.

点赞