Когда вы вызываете std::generate
, он получает собственную копию объекта функтора. Оказавшись внутри этой функции, он просто вызывает свой отдельный экземпляр объекта несколько раз, поэтому состояние сохраняется внутри вызова 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
будет обновлено на месте.
Как указывает Джерри Коффин, сохранение состояния даже внутри вызова не гарантируется, поэтому разумно делать что-то подобное с функторами с состоянием, даже если вам не нужно сохранять состояние для вызывающий.