Почему for_each не может изменить аргумент функтора? - PullRequest
8 голосов
/ 20 января 2010

http://www.cplusplus.com/reference/algorithm/for_each/
Унарная функция, принимающая элемент в диапазон в качестве аргумента. Это может либо быть указателем на функцию или объект, класс которого перегружается оператор (). Возвращаемое значение, если есть, игнорируется.

Согласно этой статье, я ожидал, что for_each фактически изменяет объект, заданный в качестве третьего аргумента, но похоже, что for_each работает с временным объектом и даже не изменяет данный ему объект.

Итак, почему это реализовано таким образом? Это кажется гораздо менее полезным. Или я что-то неправильно понял, и мой код ниже содержит ошибки?

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

template <class T> struct Multiplicator{
    T mresult;
  public:
    const T& result() const{return mresult;}
    Multiplicator(T init_result = 1){
      mresult = init_result;
    }
    void operator()(T element){
      mresult *= element;
      std::cout << element << " "; // debug print
    }
};

int main()
{
    std::vector<double> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
    Multiplicator<double> multiply;
    std::for_each(vec.begin(),vec.end(),multiply);
    std::cout << "\nResult: " << multiply.result() << std::endl;
    return 0;
}

Ожидаемый результат:

1 2 3 Result: 6

Но получил следующий вывод:

1 2 3 Result: 1

Ответы [ 3 ]

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

Объект функции берется по значению. for_each возвращает объект функции, поэтому если вы измените его на:

multiply = std::for_each(vec.begin(),vec.end(),multiply);

вы получите ожидаемый результат.

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

Хотя Джеймс прав, использование std::accumulate с std::multiplies будет более правильным, вероятно:

#include <iostream>
#include <functional>
#include <numeric>
#include <vector>

int main(void)
{
    std::vector<double> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);

    double result = std::accumulate(vec.begin(), vec.end(),
                                    1.0, std::multiplies<double>());

    std::cout << "\nResult: " << result << std::endl;

}

С вашей for_each версией вам больше не нужно копировать функтор, скорее:

double result = std::for_each(vec.begin(), vec.end(), multiply).result();

Или C ++ 0x, для развлечения:

double result = 1;
std::for_each(vec.begin(), vec.end(), [&](double pX){ result *= pX; });
0 голосов
/ 20 января 2010

Семантика For_each не вписывается в то, что вы пытаетесь сделать. Накопить делает именно то, что вы пытаетесь, используйте вместо этого.

...