我是 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/