Почему std :: for_each - не модифицирующая последовательность операций? - PullRequest
10 голосов
/ 19 марта 2009

Я только что прочитал в стандарте C ++, что std::for_each - это не модифицирующая последовательность операций, наряду с find, search и так далее. Означает ли это, что функция, применяемая к каждому элементу, не должна их изменять? Это почему? Что может пойти не так?

Вот пример кода, где последовательность модифицируется. Вы видите что-то не так с этим?

void foo(int & i)
{
    i = 12;
}

int main()
{
    std::vector<int> v;
    v.push_back(0);

    std::for_each(v.begin(), v.end(), foo);
    // v now contains 12
}

Я подозреваю, что это просто вопрос интерпретации, но я хотел бы узнать ваше мнение по этому поводу.

PS: я знаю, что мог бы использовать std::transform вместо for_each, но это не главное.

Ответы [ 4 ]

20 голосов
/ 19 марта 2009

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

Вы можете изменить элемент, если он не меняет структуру контейнера (например, порядок элементов в контейнере).

[дополнение]


Обратите внимание, что, похоже, существует некоторая путаница в том, что for_each является алгоритмом "без модификации". Эта запутанная ситуация вкратце описана Страуструпом для 4-го издания «Языка программирования C ++, 3-е издание». ( CPL ) позволяет сказать, может ли for_each изменить элементы последовательности (http://www.research.att.com/~bs/3rd_printing5.html):

).

"Алгоритм for_each() классифицируется как немодифицирующий, поскольку он не изменяет последовательность явно. Однако, если он применяется к неконстантной последовательности, for_each() может изменить элементы последовательности. Например, см. использование negate() в 11,9 ". (недавнее разрешение стандартов).

В CPL изначально указывалось, что функции или объекту функции, переданному в for_each, не разрешено изменять переданный ей элемент. Однако, CPL был написан и первоначально опубликован до того, как стандарт был завершен, и, очевидно, это ограничение на for_each() было снято до того, как было завершено.

Смотри также:

16 голосов
/ 19 марта 2009

См. отчет о дефектах они говорят

LWG считает, что ничто в стандарте не запрещает функциональные объекты, которые изменяют элементы последовательности. Проблема в том, что for_each находится в разделе, озаглавленном «немутирующие алгоритмы», и заголовок может сбить с толку. Ненормативная записка должна прояснить это.

Но также обратите внимание на этот .

Кажется, они называют это "немодифицирующим", потому что for_each сам по себе не изменяет явно элементы последовательности.

6 голосов
/ 19 марта 2009

Я думаю, что «немодифицирующие операции последовательности» означают, что эта операция не изменяет последовательность . Но операция может изменить элементы контейнера .

Значение элементов и последовательности контейнера - разные вещи.

2 голосов
/ 20 марта 2009

Как указано выше, for_each классифицируется как «алгоритм немутации»

'Мутирующим' аналогом STL является std :: transform.

Поскольку вы указали, что знаете, что можете использовать std :: transform, вышеприведенное действительно становится точкой. Он служит точкой связи с людьми, которые читают ваш код.

Если я увижу std :: for_each, ясно, что, что бы ни делал foo, он не изменит контейнер.

Указание, которому я следую, может быть сформулировано:

"Если вы хотите использовать элементы контейнера для выполнения какой-либо задачи, которая не изменяет элементы, используйте std :: for_each.

Если вы хотите использовать элементы контейнера для системного изменения элементов или использовать их в задании, которое каким-либо образом изменит их, используйте std :: transform. "

...