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

标签 c++ function object

我是 C++ 菜鸟,正在研究仿函数。我有如下代码(注意——这不是我的作业,我已经过去了!)。

它确实在控制台上打印 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() 通过引用/指针调用仿函数?

进一步阅读 Material 表示赞赏。

最佳答案

当您调用 std::generate 时,它会获得自己的仿函数对象拷贝。但是,一旦进入该函数,它只是重复调用它自己的对象的单个实例,因此状态在 inside generate 调用中保留,但不是generate 和调用者之间。

因此,将您的代码更改为

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

之后 v1.n 仍将为零。在 generate 内部,它正在对其本地拷贝(例如 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 指出的那样,即使在内部 调用中也不能保证状态的保存,因此即使您不需要为来电者。

关于c++ - 仿函数如何维护/存储对象的状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9380190/

相关文章:

c++ - 与 g_signal_handlers_block_by_func() 等价的 gtkmm 是什么?

php - 在 PHP 中精确舍入和舍入分数的函数,有更好的方法吗?

javascript - 在 JavaScript 中通过字符串名称引用对象

c++ - 为什么运算符重载实现中的两个静态对象在 C++ 中总是相等的?

c++ - 使用许多静态成员有问题吗?

c++ - '/usr/include/c++/4.4/bits/'中的位是什么意思

c++ - 填充指针 vector

postgresql - 使用变量作为语句的一部分

mysql - 子查询返回多于 1 行(Sql 函数)

javascript - 使用对象数组中的相同键创建值数组