Подтвердить что ты не робот

В чем разница между std:: transform и std:: for_each?

Оба могут использоваться для применения функции к ряду элементов.

На высоком уровне:

  • std::for_each игнорирует возвращаемое значение функции и гарантирует порядок исполнения.
  • std::transform присваивает возвращаемое значение итератору и делает не гарантирует порядок выполнения.

Когда вы предпочитаете использовать один против другого? Есть ли какие-то тонкие оговорки?

4b9b3361

Ответ 1

std::transform совпадает с map. Идея состоит в том, чтобы применить функцию к каждому элементу между двумя итераторами и получить другой контейнер, состоящий из элементов, возникающих в результате применения такой функции. Вы можете использовать его для, например, проецирования элемента данных объекта в новый контейнер. В дальнейшем std::transform используется для преобразования контейнера std::string в контейнер std::size_t s.

std::vector<std::string> names = {"hi", "test", "foo"};
std::vector<std::size_t> name_sizes;

std::transform(names.begin(), names.end(), std::back_inserter(name_sizes), [](const std::string& name) { return name.size();});

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

Вернемся к примеру строки:

std::for_each(name_sizes.begin(), name_sizes.end(), [](std::size_t name_size) {
    std::cout << name_size << std::endl;
});

В самом деле, начиная с С++ 11, то же может быть достигнуто с помощью обозначения терминов с использованием циклы for на основе диапазона:

for (std::size_t name_size: name_sizes) {
    std::cout << name_size << std::endl;
}

Ответ 2

Ваш обзор высокого уровня

  • std::for_each игнорирует возвращаемое значение функции и гарантирует порядок выполнения.
  • std::transform присваивает возвращаемое значение итератору и не гарантирует порядок выполнения.

в значительной степени покрывает его.

Другой способ взглянуть на него (предпочесть один над другим);

  • Получают ли результаты (возвращаемое значение) операции?
  • Является ли операция для каждого элемента методом члена без возвращаемого значения?
  • Существуют ли два диапазона ввода?

Еще одна вещь, о которой нужно помнить (тонкое предостережение) - это изменение требований операций std::transform до и после С++ 11 (из en.cppreference.com);

  • До С++ 11 они должны были "не иметь побочных эффектов",
  • После С++ 11 это изменилось на "не должно отменять никаких итераторов, включая конечные итераторы, или изменять любые элементы используемых диапазонов"

В основном это должно было обеспечить неопределенный порядок выполнения.

Когда я использую один над другим?

Если я хочу манипулировать каждым элементом в диапазоне, я использую for_each. Если мне нужно вычислить что-то из каждого элемента, я бы использовал transform. При использовании for_each и transform я обычно соединяю их с лямбдой.

Тем не менее, я считаю, что мое текущее использование традиционного for_each несколько уменьшилось с момента появления диапазонов на основе for и lambdas в С++ 11 (for (element : range)). Я считаю, что его синтаксис и реализация очень естественны (но ваш пробег здесь будет отличаться) и более интуитивно понятным для некоторых случаев использования.

Ответ 3

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

for_each относится к не изменяющим STL операциям, что означает, что эти операции не меняют элементы коллекции или самой коллекции. Поэтому значение , возвращаемое for_each, всегда игнорируется и не присваивается элементу коллекции. Тем не менее, по-прежнему можно изменять элементы коллекции, например, когда элемент передается функции f с использованием ссылки. Следует избегать такого поведения, поскольку оно не согласуется с принципами STL.

В отличие от этого функция transform относится к модификации операций STL и применяет заданные предикаты (unary_op или binary_op) к элементам коллекции или коллекций и сохраняет результаты в другой коллекции.

#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

void printer(int i) {
        cout << i << ", ";
}
int main() {
    int mynumbers[] = { 1, 2, 3, 4 };
    vector<int> v(mynumbers, mynumbers + 4);

    for_each(v.begin(), v.end(), negate<int>());//no effect as returned value of UnaryFunction negate() is ignored.
    for_each(v.begin(), v.end(), printer);      //guarantees order

    cout << endl;

    transform(v.begin(), v.end(), v.begin(), negate<int>());//negates elements correctly
    for_each(v.begin(), v.end(), printer);
    return 0;
}

который будет печатать:

1, 2, 3, 4, 
-1, -2, -3, -4, 

Ответ 4

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

std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);

если вы попытаетесь достичь того же с помощью std:: for_each, например:

std::for_each(s.begin(), s.end(), ::toupper);

Он не конвертирует его в верхнюю строку