函子如何维护/存储对象的状态
How does the functor maintain/store state of a 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;
}
因为我看到了下面这样的代码,它在函子中使用引用变量来保持状态,所以在这里,我使用这个概念开发了一个简单的代码,如下所示:
示例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);
并且之后CCD_ 4将仍然为零。在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
共享相同的状态,并且该状态将在调用后可访问。
事实上,我们可以编写一个简单的ish包装器来实现自动化,所以您不需要为每个函子手工完成:
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));
意味着CCD_ 9将被更新到位。
正如Jerry Coffin所指出的,即使在调用内部也不能保证状态的保留,因此,即使不需要为调用方保留状态,也可以使用有状态函子来执行这样的操作。
当然,函子对象没有任何特殊的魔力,这与其他对象不同。但我看不出在您的例子中,函子在copting时应该保存什么状态。
考虑第一个:generate
可能实现
template <typename Iterator, typename Functor>
void generate(Iterator begin, Iterator end, Functor f)
{
for (Iterator it = begin; it != end; ++it) {
*it = f();
}
}
在这个例子中,函子在函数入口只复制了一次,而代码处理局部变量f
,并且它不执行任何复制。
当您的functor
具有memeber n
时,状态会保存在其中。
函数与任何其他对象完全一样-如果它们的成员被定义为引用,则通过引用存储;如果它被定义为值,则存储值。您的第一个示例之所以有效,是因为std::generate通过值而不是引用来获取其函子参数,因此会对您在具有g()
的表达式中创建的临时的副本进行操作。
generate函数获取函子的一个实例,并反复调用它。因此,状态保存与每个正常类中的状态保存相同。我从我的编译器(gcc 4.5)标题中删除(并简化)了这一点:
template<typename _ForwardIterator, typename _Generator>
void
generate(_ForwardIterator __first, _ForwardIterator __last,
_Generator __gen)
{
// concept requirements -- ommitted for easy reading
for (; __first != __last; ++__first)
*__first = __gen();
}
正如您所看到的,__gen将是您的例子中函子的一个实例。
请注意,我的编译器优化了您的第一个示例,因此没有进行复制构造。当使用命名变量时,会发生复制构造。
这取决于常见但不能保证的行为。具体来说,它依赖于generate
将为分配的每个值重新调用函数对象,类似于以下内容:
template <class FwdIt, class Generator>
void generate(FwdIt first, FwdIt last, Generator gen) {
while (first != last) {
*first = gen();
++first;
}
}
我相信标准允许这样做,但我很确定它不能保证。至少根据我对标准的阅读,它可以完全接受按照这个通用顺序做一些事情:
template <class FwdIt, class Generator>
void generate(FwdIt first, FwdIt last, Generator gen) {
decltype(*first) holder = gen();
while (first != last) {
*first = holder;
++first;
}
}
在这种情况下,范围中的每个项目都将被分配相同的值。也就是说,这似乎是实现generate
的一种非常不寻常的方式。我很确定这是允许的,但不认为有太多的理由这么做。
同时,我应该指出,有几个小提示可以说明这样做的原因。第一个是效率:存储价值可能比重新创造N次更便宜。
第二种是基于对标准(§25.2.6/1)中描述的仔细(迂腐)阅读:
效果:调用函数对象
gen
,并通过范围[first, last)
或[first, first + n)
中的所有迭代器分配gen
的返回值。
考虑到它的措辞,你可能会争辩说,它基本上是说你只调用gen
一次,然后将一个返回值分配给范围内的所有迭代器,而不是为范围内的每个迭代器重新调用它。例如,它谈到了"返回值",这意味着只有一个值,而不是范围中每个迭代器的单独返回值。
编辑:重读一遍,我认为这个标准确实有力地表明了第一个是有意的。往下读,我们得到:
复杂性:完全是最后一次(或n次)调用gen和赋值。
如果您有意反常,您仍然可以分配单个调用的所有值,并忽略其他调用的返回,但这确实清楚地表明,像上面第一个这样的实现是预期的(第二个实现的不符合)。
示例1之所以有效,是因为函子对象(v1)有一个成员变量(n),每次调用该对象时该变量都会递增。
示例2的不同之处在于,函子对象(v1)只更新对位于对象外部的变量的引用。
示例1是一个更好的面向对象设计,因为您创建的g类的每个对象都将负责自己的计数,而在示例2中,调用者有责任确保计数器不共享,并且至少与函子对象具有相同的寿命。
- 如何使用 stl 容器有效地存储对象?(即根据其字段的值进行搜索)
- 线程上下文上的静态存储对象优化
- OOP - 存储对象和服务接口
- 为什么存储对象地址在缓冲区中会导致内存泄漏并删除它们
- 是否保证在销毁所有线程本地存储对象后销毁全局对象
- 使用列表存储对象
- 在c++中存储一个对象或不存储对象的首选方式是什么
- 更好的是:存储对象与存储指针
- 应用程序使用QMAP存储对象时停止响应
- 使用堆栈存储对象
- 如何在 std::map 中存储"对象类型"?
- 我应该如何存储对象
- 在 c++ 中存储对象的最佳策略是什么,确保名称唯一并能够在以后有效地检索它们
- 用于存储对象列表的最佳标准类
- 在C++中存储对象地址的数据结构
- 函子如何维护/存储对象的状态
- 我如何定义一个向量,并用C++在其中存储对象
- 存储对象以备将来使用(在重新启动程序时)
- C++/JNI-存储对象(jobject)在向量和数组中发生意外更改,C++或JNI问题
- 在多个容器中存储对象的最佳实践