2010-01-20 4 views
8

http://www.cplusplus.com/reference/algorithm/for_each/
Унарная функция, принимающая элемент в диапазон как аргумент. Это может быть быть указателем на функцию или объект , класс перегрузки которого operator(). Его возвращаемое значение, если оно есть, игнорируется.Почему переменная 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 
+0

Как насчет изменения названия вопроса? Может облегчить для других поиск. – BeeBand

ответ

15

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

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

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

+1

Как правило, для функторов, и поскольку они должны быть легкими и скопируемыми, я обманываю и использую атрибут, который ссылается на переменную, переданную в конструктор. Таким образом, любая копия является строго эквивалентной и относится к одному и тому же элементу. Конечно, это делает инициализацию немного неудобной (объявляйте переменную, передавайте ее функтору, читайте из переменной), но она отлично работает и не требует каких-либо конкретных приемов реализации (например, не отбрасывает только что измененный функтор). –

+0

Lemme дает вам первый золотой значок STL –

+0

@Johannes: Спасибо, но я должен ответить еще на 36 вопросов с тегами [stl] first :-) (требование теперь не менее 1000 upvotes и не менее 200 ответов, я помню, когда они изменили его год назад, и я потерял свой золотой значок [c] в течение пары месяцев.) –

10

В то время как Джеймс правильно, используя 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

Что это такое, «предложите правильный инструмент для рабочего дня?» :-) +1 –

+0

Спасибо за подсказку. – smerlin

+0

@James: 8) К сожалению, качество моих ответов экспоненциально уменьшается по мере того, как день продолжается. :п – GManNickG

0

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

Смежные вопросы