{"id":1541,"date":"2016-06-20T07:23:44","date_gmt":"2016-06-20T06:23:44","guid":{"rendered":"http:\/\/netrix.org.pl\/?p=1541"},"modified":"2016-06-20T07:23:44","modified_gmt":"2016-06-20T06:23:44","slug":"range-based-for-loop-vs-stdtransform","status":"publish","type":"post","link":"https:\/\/netrix.org.pl\/index.php\/2016\/06\/20\/range-based-for-loop-vs-stdtransform\/","title":{"rendered":"Range-based for loop vs std::transform"},"content":{"rendered":"<p>Some time ago, while doing some code review in work I came across the problem whether the loop in the code I was reviewing should be expressed with <em>std::for_each<\/em>, <em>std::transform<\/em> or <em>range-based for loop<\/em>. The initial code looked pretty much like this:<\/p>\n<pre lang=\"cpp\" line=\"1\">std::vector<Type> destination;\r\ndestination.reserve(source.size());\r\n\r\nstd::for_each(std::begin(source), std::end(source), \r\n              [&destination](auto const& p_value)\r\n{\r\n    destination.push_back(SomeStruct{p_value.a, p_value.b, p_value.c});\r\n});\r\n<\/pre>\n<p>The first things that come to my mind when I see such code are &#8220;Why are you using <em>std::for_each<\/em>? Shouldn&#8217;t you use <em>range-based for<\/em> loop?&#8221; Or maybe there should be a <em>std::transform<\/em>?&#8221; Let&#8217;s consider those three things:<\/p>\n<h4><em>std::for_each<\/em><\/h4>\n<p>This is an algorithm from standard library that represents standard for loop that goes over a range defined by begin and end iterator and runs given functor or function. An example of usage is presented above. It utilizes lambda expression that invokes push_back on a std::vector to add elements.<\/p>\n<h4><em>range-based for<\/em><\/h4>\n<p>Given algorithm can be easily converted to this new form of loop (introduced in C++11).<\/p>\n<pre lang=\"cpp\" line=\"1\">\r\nstd::vector<Type> destination;\r\ndestination.reserve(source.size());\r\n\r\nfor(auto const& p_value : source) \r\n{\r\n    destination.push_back(SomeStruct{p_value.a, p_value.b, p_value.c});\r\n};\r\n<\/pre>\n<p>This is the same algorithm but in much clearer and less noisy form.<\/p>\n<h4><em>std::transform<\/em><\/h4>\n<p>Another considered algorithm is <em>std::transform<\/em> that is supposed to be used (as the name says) to transform one structure to another. Let&#8217;s convert given code to utilize this algorithm.<\/p>\n<pre lang=\"cpp\" line=\"1\">std::vector<Type> destination;\r\ndestination.reserve(source.size());\r\n\r\nstd::transform(std::begin(source), std::end(source), std::back_inserter(destination),\r\n              [](auto const& p_value)\r\n{\r\n    return SomeStruct{p_value.a, p_value.b, p_value.c};\r\n});<\/pre>\n<p>It looks pretty much like <em>std::for_each<\/em>. The difference is in the name, the third argument and that the lambda returns new value instead of inserting in into the new container.<\/p>\n<p>To be honest, when I have to choose between those versions I would choose <em>std::transform<\/em> because of its explicitness. This algorithm just says what is done. It gets one array and transforms it into another. <em>std::for_each<\/em> and <em>range-based-for<\/em> are just simple loops with no more special meaning than &#8220;run this function over this array&#8221;. From the last two if I had to choose one, then I would prefer <em>range-based-for<\/em> since its very clean form of loop over an array (no additional noise in form of lambda or <em>std::begin\/std::end<\/em>).<\/p>\n<h4>But&#8230;<\/h4>\n<p>Since we are considering C++11 there&#8217;s one more thing to consider when inserting elements into <em>std::vector<\/em> &#8211; <em>emplace_back<\/em>. If we have a class with constructor and <em>emplace_back<\/em> then situation changes, because <em>emplace_back<\/em> allows us to create objects right inside the vector&#8217;s memory. In that case using <em>std::transform<\/em> doesn&#8217;t allow such operation because it requires copy (inside <em>std::back_inserter<\/em>). <\/p>\n<p>Let&#8217;s take look at the code with all scenarios:<\/p>\n<pre lang=\"cpp\" line=\"1\">\r\n#include <iostream>\r\n#include <algorithm>\r\n#include <vector>\r\n\r\nstruct TestStruct\r\n{\r\n    TestStruct(int a, std::string b)\r\n        : m_a(a)\r\n        , m_b(b)\r\n    {\r\n        std::cout << \">> Constructor: a=\" << m_a << \", b=\" << m_b << std::endl;\r\n    }\r\n\r\n    TestStruct(TestStruct const&#038; src)\r\n        : m_a(src.m_a)\r\n        , m_b(src.m_b)\r\n    {\r\n        std::cout << \">> Copy Constructor: a=\" << m_a << \", b=\" << m_b << std::endl;\r\n    }\r\n\r\n    TestStruct(TestStruct &#038;&#038; src)\r\n        : m_a(src.m_a)\r\n        , m_b(std::move(src.m_b))\r\n    {\r\n        std::cout << \">> Move Constructor: a=\" << m_a << \", b=\" << m_b << std::endl;\r\n    }\r\n\r\n    ~TestStruct()\r\n    {\r\n        std::cout << \">> Destructor: a=\" << m_a << \", b=\" << m_b << std::endl;\r\n    }\r\n\r\nprivate:\r\n    int m_a;\r\n    std::string m_b;\r\n};\r\n\r\nstruct InputStruct\r\n{\r\n    int a;\r\n    std::string b;\r\n};\r\n\r\nusing namespace std;\r\n\r\nint main(int argc, char *argv[])\r\n{\r\n    InputStruct input[] = {\r\n        { 1, \"one\" },\r\n        { 2, \"two\" },\r\n    };\r\n\r\n    std::cout << \"std::for_each:\" << std::endl;\r\n\r\n    std::vector<TestStruct> va;\r\n    va.reserve(std::end(input) - std::begin(input));\r\n    std::for_each(std::begin(input), std::end(input),\r\n                  [&va](auto const& p_in)\r\n    {\r\n        va.push_back(TestStruct{p_in.a, p_in.b});\r\n    });\r\n\r\n    std::cout << \"\\nrange-based-for:\" << std::endl;\r\n\r\n    std::vector<TestStruct> vb;\r\n    va.reserve(std::end(input) - std::begin(input));\r\n    for(auto const& p_in : input)\r\n    {\r\n        vb.push_back(TestStruct{p_in.a, p_in.b});\r\n    }\r\n\r\n    std::cout << \"\\nstd::transform:\" << std::endl;\r\n    std::vector<TestStruct> vc;\r\n    vc.reserve(std::end(input) - std::begin(input));\r\n    std::transform(std::begin(input), std::end(input), std::back_inserter(vc),\r\n                  [](auto const& p_in)\r\n    {\r\n        return TestStruct{p_in.a, p_in.b};\r\n    });\r\n\r\n    std::cout << \"\\nrange-based-for + emplace_back:\" << std::endl;\r\n\r\n    std::vector<TestStruct> vd;\r\n    vd.reserve(std::end(input) - std::begin(input));\r\n    for(auto const& p_in : input)\r\n    {\r\n        vd.emplace_back(p_in.a, p_in.b);\r\n    }\r\n\r\n    std::cout << \"\\n\\nDestruction of all items:\" << std::endl;\r\n}\r\n\r\n<\/pre>\n<p>The output is as follows:<\/p>\n<pre line=\"1\">std::for_each:\r\n>> Constructor: a=1, b=one\r\n>> Move Constructor: a=1, b=one\r\n>> Destructor: a=1, b=\r\n>> Constructor: a=2, b=two\r\n>> Move Constructor: a=2, b=two\r\n>> Destructor: a=2, b=\r\n\r\nrange-based-for:\r\n>> Constructor: a=1, b=one\r\n>> Move Constructor: a=1, b=one\r\n>> Destructor: a=1, b=\r\n>> Constructor: a=2, b=two\r\n>> Move Constructor: a=2, b=two\r\n>> Destructor: a=2, b=\r\n\r\nstd::transform:\r\n>> Constructor: a=1, b=one\r\n>> Move Constructor: a=1, b=one\r\n>> Destructor: a=1, b=\r\n>> Constructor: a=2, b=two\r\n>> Move Constructor: a=2, b=two\r\n>> Destructor: a=2, b=\r\n\r\nrange-based-for + emplace_back:\r\n>> Constructor: a=1, b=one\r\n>> Constructor: a=2, b=two\r\n\r\n\r\nDestruction of all items:\r\n>> Destructor: a=1, b=one\r\n>> Destructor: a=2, b=two\r\n>> Destructor: a=1, b=one\r\n>> Destructor: a=2, b=two\r\n>> Destructor: a=1, b=one\r\n>> Destructor: a=2, b=two\r\n>> Destructor: a=1, b=one\r\n>> Destructor: a=2, b=two\r\n<\/pre>\n<p>The output from the app reveals everything. The <em>emplace_back<\/em> version produces the smallest output, just as suspected, and all other loops does exactly the same thing.<\/p>\n<h4>What about POD?<\/h4>\n<p>Of course the previous version works smoothly only with classes that defines the constructor which can be used within <em>emplace_back<\/em>. When considering POD structs that doesn't have one, the trick won't work and won't even compile. When adding POD object into vector it requires explicit use of the struct name when creating new objects that goes to the container.<\/p>\n<p>In such case, in my opinion, the <em>std::transform<\/em> is the best way to express the intent.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Some time ago, while doing some code review in work I came across the problem whether the loop in the code I was reviewing should be expressed with std::for_each, std::transform or range-based for loop. The initial code looked pretty much like this: std::vector destination; destination.reserve(source.size()); std::for_each(std::begin(source), std::end(source), [&#038;destination](auto const&#038; p_value) { destination.push_back(SomeStruct{p_value.a, p_value.b, p_value.c}); }); [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[205,179,206],"_links":{"self":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/1541"}],"collection":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/comments?post=1541"}],"version-history":[{"count":14,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/1541\/revisions"}],"predecessor-version":[{"id":1560,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/1541\/revisions\/1560"}],"wp:attachment":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/media?parent=1541"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/categories?post=1541"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/tags?post=1541"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}