<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	
	>
<channel>
	<title>
	Comments on: Algorithms on Ranges	</title>
	<atom:link href="https://www.fluentcpp.com/2018/12/07/algorithms-on-ranges/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.fluentcpp.com/2018/12/07/algorithms-on-ranges/</link>
	<description>Jonathan Boccara&#039;s blog</description>
	<lastBuildDate>Sun, 09 Dec 2018 13:31:06 +0000</lastBuildDate>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.5.3</generator>
	<item>
		<title>
		By: Jonathan Boccara		</title>
		<link>https://www.fluentcpp.com/2018/12/07/algorithms-on-ranges/#comment-1524</link>

		<dc:creator><![CDATA[Jonathan Boccara]]></dc:creator>
		<pubDate>Sun, 09 Dec 2018 04:50:00 +0000</pubDate>
		<guid isPermaLink="false">https://www.fluentcpp.com/?p=4423#comment-1524</guid>

					<description><![CDATA[In reply to &lt;a href=&quot;https://www.fluentcpp.com/2018/12/07/algorithms-on-ranges/#comment-1521&quot;&gt;Vincent Zalzal&lt;/a&gt;.

Thanks for these interesting observations Vincent!]]></description>
			<content:encoded><![CDATA[<p>In reply to <a href="https://www.fluentcpp.com/2018/12/07/algorithms-on-ranges/#comment-1521">Vincent Zalzal</a>.</p>
<p>Thanks for these interesting observations Vincent!</p>
]]></content:encoded>
		
			</item>
		<item>
		<title>
		By: Vincent Zalzal		</title>
		<link>https://www.fluentcpp.com/2018/12/07/algorithms-on-ranges/#comment-1523</link>

		<dc:creator><![CDATA[Vincent Zalzal]]></dc:creator>
		<pubDate>Sat, 08 Dec 2018 19:13:00 +0000</pubDate>
		<guid isPermaLink="false">https://www.fluentcpp.com/?p=4423#comment-1523</guid>

					<description><![CDATA[When doing an algorithm wrapper like this, another consideration to keep in mind is ADL (&lt;a href=&quot;https://en.cppreference.com/w/cpp/language/adl&quot; rel=&quot;nofollow&quot;&gt;argument-dependent lookup&lt;/a&gt;). In our codebase, we have legacy containers that have non-compliant Begin() and End() member functions. To enable range-based for loops on them, we added begin() and end() non-member functions. If an algorithm wrapper like copy_if is to work both with std containers and legacy containers, ADL must be enabled with using-declarations, the same way one does with the swap function.

&lt;pre&gt;&lt;code class=&quot;C++&quot;&gt;
template&#060;typename InputRange, typename OutputIterator, typename Predicate&#062;
constexpr auto copy_if(InputRange&#038; range,
                       OutputIterator out,
                       Predicate pred)
{
    // Enable ADL
    using std::begin;
    using std::end;
    return std::copy_if(begin(range), end(range), out, pred);
}
&lt;/code&gt;&lt;/pre&gt;]]></description>
			<content:encoded><![CDATA[<p>When doing an algorithm wrapper like this, another consideration to keep in mind is ADL (<a href="https://en.cppreference.com/w/cpp/language/adl" rel="nofollow">argument-dependent lookup</a>). In our codebase, we have legacy containers that have non-compliant Begin() and End() member functions. To enable range-based for loops on them, we added begin() and end() non-member functions. If an algorithm wrapper like copy_if is to work both with std containers and legacy containers, ADL must be enabled with using-declarations, the same way one does with the swap function.</p>
<pre><code class="C++">
template&lt;typename InputRange, typename OutputIterator, typename Predicate&gt;
constexpr auto copy_if(InputRange&amp; range,
                       OutputIterator out,
                       Predicate pred)
{
    // Enable ADL
    using std::begin;
    using std::end;
    return std::copy_if(begin(range), end(range), out, pred);
}
</code></pre>
]]></content:encoded>
		
			</item>
		<item>
		<title>
		By: Vincent Zalzal		</title>
		<link>https://www.fluentcpp.com/2018/12/07/algorithms-on-ranges/#comment-1522</link>

		<dc:creator><![CDATA[Vincent Zalzal]]></dc:creator>
		<pubDate>Sat, 08 Dec 2018 18:59:00 +0000</pubDate>
		<guid isPermaLink="false">https://www.fluentcpp.com/?p=4423#comment-1522</guid>

					<description><![CDATA[By the way, an &quot;okay&quot; C++17 alternative to passing ranges by const reference is to use std::as_const to ensure a range is not modified.

&lt;pre&gt;&lt;code class=&quot;C++&quot;&gt;
std::vector&#060;int&#062; v{1, 2, 3, 4, 5};
std::vector&#060;int&#062; o;

auto nonMutatingPredicate  = [](int  i) {return true;};
auto evilMutatingPredicate = [](int&#038; i) {i = -1; return true;};

copy_if(v, std::back_inserter(o), nonMutatingPredicate);
copy_if(v, std::back_inserter(o), evilMutatingPredicate);

copy_if(std::as_const(v), std::back_inserter(o), nonMutatingPredicate);
//copy_if(std::as_const(v), std::back_inserter(o), evilMutatingPredicate); // error: binding reference of type &#039;int&#038;&#039; to &#039;const int&#039; discards qualifiers
&lt;/code&gt;&lt;/pre&gt;]]></description>
			<content:encoded><![CDATA[<p>By the way, an &#8220;okay&#8221; C++17 alternative to passing ranges by const reference is to use std::as_const to ensure a range is not modified.</p>
<pre><code class="C++">
std::vector&lt;int&gt; v{1, 2, 3, 4, 5};
std::vector&lt;int&gt; o;

auto nonMutatingPredicate  = [](int  i) {return true;};
auto evilMutatingPredicate = [](int&amp; i) {i = -1; return true;};

copy_if(v, std::back_inserter(o), nonMutatingPredicate);
copy_if(v, std::back_inserter(o), evilMutatingPredicate);

copy_if(std::as_const(v), std::back_inserter(o), nonMutatingPredicate);
//copy_if(std::as_const(v), std::back_inserter(o), evilMutatingPredicate); // error: binding reference of type 'int&amp;' to 'const int' discards qualifiers
</code></pre>
]]></content:encoded>
		
			</item>
		<item>
		<title>
		By: Vincent Zalzal		</title>
		<link>https://www.fluentcpp.com/2018/12/07/algorithms-on-ranges/#comment-1521</link>

		<dc:creator><![CDATA[Vincent Zalzal]]></dc:creator>
		<pubDate>Sat, 08 Dec 2018 17:55:00 +0000</pubDate>
		<guid isPermaLink="false">https://www.fluentcpp.com/?p=4423#comment-1521</guid>

					<description><![CDATA[At work, I did my own thin STL wrapper as suggested in this article. At first, I decided to take ranges by const reference for algorithms not supposed to modify the range. However, usage has shown that it is often problematic. A problem I found is when a non-mutating algorithm returns an iterator. The simplest case is std::find(). It is expected that std::find() should not modify the input range. However, the returned iterator of std::find() should have the same constness as the input range.

&lt;pre&gt;&lt;code class=&quot;C++&quot;&gt;
template 
inline auto findByConstRef(const InputRange&#038; range, const T&#038; value)
{
    return std::find(begin(range), end(range), value);
}

void testFindByConstRef()
{
    std::vector v {1, 2, 3, 4, 5};
    //*findByConstRef(v, 3) = -1; // error: assignment of read-only location
}
&lt;/code&gt;&lt;/pre&gt;

In the code above, most users would expect to be able to modify the range using the iterator returned by findByConstRef().

Because of this and problems like those pointed out in this article, I decided, for consistency, to never take a container by const reference in my wrapper.

The &quot;obvious&quot; alternative is to use forwarding references. However, forwarding references bind to temporary ranges. This can lead to potentially dangerous or unwanted behavior if the programmer is not careful.

&lt;pre&gt;&lt;code class=&quot;C++&quot;&gt;
template 
inline auto findByRefRef(InputRange&#038;&#038; range, const T&#038; value)
{
    return std::find(begin(range), end(range), value);
}

std::vector getTempVec()
{
    return {1, 2, 3, 4, 5};
}

void testFindByRefRef()
{
    auto it = findByRefRef(getTempVec(), 3);
    *it = -1; // undefined behavior
}
&lt;/code&gt;&lt;/pre&gt;

In the classic, iterator-based STL, it is not possible to use temporary ranges anyway, because both begin() and end() must be called on the same range, forcing it to have a name. So I decided to avoid forwarding references too, and thus sacrificed the potential composability that comes with temporary ranges, unlike in the current ISO C++ proposal.

In the end, I chose to take ranges by reference. Because of template type deduction rules, const ranges are still usable, and their constness is preserved. However, it does not enforce an obviously const algorithm like find_if to not modify the range elements.

&lt;pre&gt;&lt;code class=&quot;C++&quot;&gt;
template 
inline auto findByRef(InputRange&#038; range, const T&#038; value)
{
    return std::find(begin(range), end(range), value);
}

std::vector getTempVec()
{
    return {1, 2, 3, 4, 5};
}

void testFindByRef()
{
    std::vector v {1, 2, 3, 4, 5};
    const std::vector cv {1, 2, 3, 4, 5};

    *findByRef(v, 3) = -1;
    //*findByRef(cv, 3) = -1;                // error: assignment of read-only location
    //*findByRef(getTempVec(), 3) = -1;      // error: cannot bind non-const lvalue reference to rvalue
}
&lt;/code&gt;&lt;/pre&gt;

You can play with the code &lt;a href=&quot;https://godbolt.org/z/_FIDjZ&quot; rel=&quot;nofollow&quot;&gt;here&lt;/a&gt;.]]></description>
			<content:encoded><![CDATA[<p>At work, I did my own thin STL wrapper as suggested in this article. At first, I decided to take ranges by const reference for algorithms not supposed to modify the range. However, usage has shown that it is often problematic. A problem I found is when a non-mutating algorithm returns an iterator. The simplest case is std::find(). It is expected that std::find() should not modify the input range. However, the returned iterator of std::find() should have the same constness as the input range.</p>
<pre><code class="C++">
template 
inline auto findByConstRef(const InputRange&amp; range, const T&amp; value)
{
    return std::find(begin(range), end(range), value);
}

void testFindByConstRef()
{
    std::vector v {1, 2, 3, 4, 5};
    //*findByConstRef(v, 3) = -1; // error: assignment of read-only location
}
</code></pre>
<p>In the code above, most users would expect to be able to modify the range using the iterator returned by findByConstRef().</p>
<p>Because of this and problems like those pointed out in this article, I decided, for consistency, to never take a container by const reference in my wrapper.</p>
<p>The &#8220;obvious&#8221; alternative is to use forwarding references. However, forwarding references bind to temporary ranges. This can lead to potentially dangerous or unwanted behavior if the programmer is not careful.</p>
<pre><code class="C++">
template 
inline auto findByRefRef(InputRange&amp;&amp; range, const T&amp; value)
{
    return std::find(begin(range), end(range), value);
}

std::vector getTempVec()
{
    return {1, 2, 3, 4, 5};
}

void testFindByRefRef()
{
    auto it = findByRefRef(getTempVec(), 3);
    *it = -1; // undefined behavior
}
</code></pre>
<p>In the classic, iterator-based STL, it is not possible to use temporary ranges anyway, because both begin() and end() must be called on the same range, forcing it to have a name. So I decided to avoid forwarding references too, and thus sacrificed the potential composability that comes with temporary ranges, unlike in the current ISO C++ proposal.</p>
<p>In the end, I chose to take ranges by reference. Because of template type deduction rules, const ranges are still usable, and their constness is preserved. However, it does not enforce an obviously const algorithm like find_if to not modify the range elements.</p>
<pre><code class="C++">
template 
inline auto findByRef(InputRange&amp; range, const T&amp; value)
{
    return std::find(begin(range), end(range), value);
}

std::vector getTempVec()
{
    return {1, 2, 3, 4, 5};
}

void testFindByRef()
{
    std::vector v {1, 2, 3, 4, 5};
    const std::vector cv {1, 2, 3, 4, 5};

    *findByRef(v, 3) = -1;
    //*findByRef(cv, 3) = -1;                // error: assignment of read-only location
    //*findByRef(getTempVec(), 3) = -1;      // error: cannot bind non-const lvalue reference to rvalue
}
</code></pre>
<p>You can play with the code <a href="https://godbolt.org/z/_FIDjZ" rel="nofollow">here</a>.</p>
]]></content:encoded>
		
			</item>
		<item>
		<title>
		By: jft		</title>
		<link>https://www.fluentcpp.com/2018/12/07/algorithms-on-ranges/#comment-1519</link>

		<dc:creator><![CDATA[jft]]></dc:creator>
		<pubDate>Fri, 07 Dec 2018 16:55:00 +0000</pubDate>
		<guid isPermaLink="false">https://www.fluentcpp.com/?p=4423#comment-1519</guid>

					<description><![CDATA[The github range-v3 library (current 0.4.0 at least is needed as this has the MSVS required code changes) is now supported by MS VS2017 v 15.9.x. See https://blogs.msdn.microsoft.com/vcblog/2018/11/07/use-the-official-range-v3-with-msvc-2017-version-15-9/ As I understand it, this is what&#039;s coming in c++20 so IMO I would just stick to what is provided in the range-v3 library so that code is compatible for c++20 and not to &#039;re-invent&#039; things.]]></description>
			<content:encoded><![CDATA[<p>The github range-v3 library (current 0.4.0 at least is needed as this has the MSVS required code changes) is now supported by MS VS2017 v 15.9.x. See <a href="https://blogs.msdn.microsoft.com/vcblog/2018/11/07/use-the-official-range-v3-with-msvc-2017-version-15-9/" rel="nofollow ugc">https://blogs.msdn.microsoft.com/vcblog/2018/11/07/use-the-official-range-v3-with-msvc-2017-version-15-9/</a> As I understand it, this is what&#8217;s coming in c++20 so IMO I would just stick to what is provided in the range-v3 library so that code is compatible for c++20 and not to &#8216;re-invent&#8217; things.</p>
]]></content:encoded>
		
			</item>
	</channel>
</rss>
