2015-06-26 217 views
23

兩者都可用於將函數應用於一系列元素。std :: transform和std :: for_each有什麼區別?

在高水平:

  • std::for_each忽略函數的返回值, 保證執行順序。
  • std::transform分配返回值迭代器,並沒有 不能保證執行順序。

如果你喜歡使用一個與其他?有沒有什麼細微的警告?

+7

'transform'具有的輸出範圍,'for_each'沒有。 – user657267

+4

很多''由非常相似的功能組成,只有*輕微*差異。 – o11c

+0

@ o11c這正是我要找的。這個細微差別肯定有很好的理由。 – bendervader

回答

27

std::transform相同map。這個想法是對兩個迭代器之間的每個元素應用一個函數,並獲得由應用這種函數產生的元素組成的不同容器。您可能希望將其用於例如將對象的數據成員投影到新的容器中。在下文中,std::transform用於將容器std::string轉換成容器std::size_ts。

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循環非常相似。

回到字符串例子:

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

對此進行擴展,在C++ 14中,我們可以從基於循環的範圍中刪除類型,現在成爲:for(name_size:name_sizes){...} –

+0

不錯!上次我檢查它只是一個建議。我通常使用'for(auto && x:xs)',但是這種抽象級別對於解釋並不重要。 –

+0

@TrevorHickey我很快再次檢查,除了最初的建議N3853之外,我找不到任何消息。你確定嗎? –

0

使用std ::變換分析是,當你想將字符串轉換爲大寫,你可以寫這樣的代碼的真實的例子:

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

,如果你將盡力達成同樣的事情的std :: for_each的像:

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

它不會將其轉換爲大寫的字符串

12

你高:

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

的確,從C++ 11相同的開始可以用更簡潔表示法使用基於範圍的for環路來實現層次概述

  • std::for_each忽略該函數的返回值並保證執行順序。
  • std::transform將返回值賦給迭代器,並不保證執行順序。

幾乎涵蓋了它。

看着它的另一種方法(優先於其他);

  • 做操作事項的結果(返回值)?
  • 對每個元素的操作是否爲無返回值的成員方法?
  • 是否有兩個輸入範圍?

一件事記(微妙的警告)來承擔是在std::transform的操作之前,和C++ 11(從en.cppreference.com)之後的要求的變化;

  • 之前C++ 11,他們被要求「沒有任何副作用」,
  • C++ 11後,該修改爲「不得違反對迭代器,其中包括結束迭代器,或修改涉及的範圍的任何元素「

基本上這些是允許未確定的執行順序。

什麼時候使用一種?

如果我想操縱某個範圍內的每個元素,那麼我使用for_each。如果我必須從每個元素計算出一些東西,那麼我會使用transform當使用for_eachtransform時,我通常將它們與lambda進行配對。

這麼說,我覺得我現在的傳統for_each的使用,因爲基於範圍的出現正在有所縮水for循環和lambda表達式在C++ 11(for (element : range))。我發現它的語法和實現非常自然(但是這裏的里程會有所不同),並且對於某些用例更直觀。

+0

你能否詳細說一下爲什麼「std :: transform ...不能保證執行的順序」? – athos

+1

這與'for_each'算法相反,它需要按順序將函數應用於每個元素。 「轉換」操作的要求沒有任何副作用意味着執行順序不可觀察,因此不能保證。這就是說,我所看到的實現都是按照預期的順序應用它,首先是最後一步。 – Niall

8

雖然問題已經得到解答,但我相信這個例子會進一步澄清這個區別。

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, 
+0

說不出效果是錯誤的。它有一個效果,即它否定整數。但是,它不存儲在內存中。 –

+0

@JossieCalderon我編輯了我的答案,我希望它現在更清晰。 – BugShotGG

相關問題