Может ли функтор сохранять значения при передаче в std :: for_each? - PullRequest
3 голосов
/ 20 января 2010

Согласно первому ответу на этот вопрос, функтор ниже должен иметь возможность сохранять значение после передачи в foreach (я не смог получить struct Accumulator в примере для компиляции так построил класс).

class Accumulator
{
    public:
        Accumulator(): counter(0){}
        int counter;
        void operator()(const Card & c) { counter += i; }
};

Пример использования (согласно примеру)

// Using a functor
Accumulator acc;
std::for_each(_cards.begin(), _cards.end(), acc);
// according to the example - acc.counter contains the sum of all
// elements of the deque 

std::cout << acc.counter << std::endl;

_cards реализован как std::deque<Card>. Независимо от того, как долго _cards получает, acc.counter равен нулю после завершения for_each. Проходя по отладчику, я вижу, что счетчик увеличивается, так что же это связано с передачей acc по значению?

Ответы [ 3 ]

6 голосов
/ 20 января 2010

Это было просто спросили здесь .

Причина в том, что (как вы уже догадались) std::for_each копирует свой функтор и вызывает его. Однако он также возвращает его, поэтому, как указано в ответе, связанном с выше, используйте возвращаемое значение для for_each.

При этом , вам просто нужно использовать std::accumulate:

int counter = std::accumulate(_cards.begin(), _cards.end(), 0);

Функтор и for_each здесь не верны.


Для вашего использования (считая одних, игнорируя другие) вам, вероятно, потребуется указать собственный функтор и использовать count_if:

// unary_function lives in <functional>
struct is_face_up : std::unary_function<const Card&, const bool>
{
    const bool operator()(const card& pC) const
    {
        return pC.isFaceUp(); // obviously I'm guessing
    }
};

int faceUp = std::count_if(_cards.begin(), _cards.end(), is_face_up());
int faceDown = 52 - faceUp;

И с C ++ 0x лямбда для развлечения (просто потому что):

int faceUp = std::count_if(_cards.begin(), _cards.end(),
                            [](const Card& pC){ return pC.isFaceUp(); });

Намного приятнее.

3 голосов
/ 20 января 2010

Это потому, что внутренне std :: for_each () создает копию функтора (так как можно передавать временный объект). Так что внутренне он делает сумму на копии, а не на объекте, который вы предоставили.

Хорошая новость заключается в том, что std :: for_each () возвращает копию функтора, чтобы вы могли получить к ней доступ оттуда.

Примечание: есть и другие стандартные алгоритмы, которые вы можете использовать. Как и std :: аккумулировать ().
Но предположим, что это просто упрощенный пример, и вам нужно for_each () для чего-то более хитрого, чем в примере. Есть несколько методов, позволяющих вам получить доступ к объекту-аккумулятору.

#include <iostream>
#include <algorithm>
#include <vector>

class Card{ public: int i;};
class Accumulator
{
    public:
        Accumulator(): counter(0){}
        int counter;
        void operator()(const Card & c) { counter += c.i; }
};


int main()
{
    std::vector<Card>   cards;

    Accumulator a = std::for_each(cards.begin(), cards.end(), Accumulator());

    std::cout << a.counter << std::endl;

}

В качестве альтернативы вы можете изменить Accumalator для увеличения ссылки, используемой в текущей области.

#include <iostream>
#include <algorithm>
#include <vector>

class Card{ public: int i;};
class Accumulator
{
        int&  counter;
    public:
        // Pass a reference to constructor.
        // Copy construction will pass this correctly into the internal object used by for_each
        Accumulator(int& counterRef): counter(counterRef){}
        void operator()(const Card & c) { counter += c.i; }
};


int main()
{
    std::vector<Card>   cards;

    int counter = 0;  // Count stored here.

    std::for_each(cards.begin(), cards.end(), Accumulator(counter));

    std::cout << counter << std::endl;

}
3 голосов
/ 20 января 2010

Да, это определенно связано с передачей по значению.

Измените ваш аккумулятор следующим образом:

class Accumulator
{
    public:
        Accumulator(): counter(new int(0)){}
        boost::shared_ptr<int> counter;
        void operator()(int i) { *counter += i; }

        int value() { return *counter; }
};
...