<!--This file created 9/6/02 4:27 PM by Claris Home Page version 2.0-->
<HTML>
<HEAD>
   <TITLE>issues 225 226 229</TITLE>
   <META NAME=GENERATOR CONTENT="Claris Home Page 2.0">
   <X-SAS-WINDOW TOP=48 BOTTOM=748 LEFT=12 RIGHT=606>
</HEAD>
<BODY BGCOLOR="#FFFFFF">

<ADDRESS>Document number: N1387=02-0045</ADDRESS>

<ADDRESS>&nbsp;</ADDRESS>

<ADDRESS>Howard E. Hinnant</ADDRESS>

<ADDRESS><A HREF="mailto:hinnant@twcny.rr.com">hinnant@twcny.rr.com</A>
</ADDRESS>

<ADDRESS>September 10, 2002</ADDRESS>

<H1>Proposed Resolution To LWG issues 225, 226, 229</H1>

<H2>Introduction</H2>

<P>A LWG subgroup met in Cura&ccedil;ao concerning these issues. Two
action items emerged, as noted in the issues list:</P>

<BLOCKQUOTE><P>[Cura&ccedil;ao: An LWG-subgroup spent an afternoon
working on issues
<A HREF="http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-active.html#225">225</A>,
<A HREF="http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-active.html#226">226</A>,
and
<A HREF="http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-active.html#229">229</A>.
Their conclusion was that the issues should be separated into an LWG
portion (Howard will write a proposal), and a EWG portion (Dave will
write a proposal). The LWG and EWG had (separate) discussions of this
plan the next day.]</P></BLOCKQUOTE>

<P>This paper is the LWG portion which will atempt to resolve these
issues within the current language.</P>

<H2>The Problem</H2>

<P>The problem can be summarized:</P>

<P>Given namespace N containing template functions f and g, with f
calling g with arguments of parameterized type, and with the intent
that client code should be able to replace g, how should the call be
made?</P>

<BLOCKQUOTE><PRE>namespace N
{
&nbsp;
template &lt;typename T&gt;
void g(T);
&nbsp;
template &lt;typename T&gt;
void
f(T t)
{
    g(t);  // or N::g(t) or something else?
}
&nbsp;
}</PRE></BLOCKQUOTE>

<P>This is an issue for the standard library in several places. For
example consider:</P>

<BLOCKQUOTE><PRE>template &lt;class ForwardIterator1, class ForwardIterator2&gt;
ForwardIterator2
swap_ranges(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2)
{
    for (; first1 != last1; ++first1, ++first2)
        swap(*first1, *first2);  // or std::swap(*first1, *first2);
    return first2;
}</PRE></BLOCKQUOTE>

<H2>Preliminary Issues</H2>

<P>In several places, an implementation of the standard library may
have one function template call another function template with no
intention of the secondary function template being replaceable by
client code. The called function is purely an implementation detail.
Issue
<A HREF="http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-active.html#225">225</A>
gives this example:</P>

<BLOCKQUOTE><PRE>template &lt;class _ForwardIter&gt;
_ForwardIter
unique(_ForwardIter __first, _ForwardIter __last)
{
    __first = adjacent_find(__first, __last);
    return unique_copy(__first, __last, __first);
}</PRE></BLOCKQUOTE>

<P>Here adjacent_find and unique_copy are implementation details,
(hopefully) clearly, std::adjacent_find and std::unique_copy are
intended. So this should be coded with explicit qualification to
disable Koenig lookup:</P>

<BLOCKQUOTE><PRE>template &lt;class _ForwardIter&gt;
_ForwardIter
unique(_ForwardIter __first, _ForwardIter __last)
{
    __first = std::adjacent_find(__first, __last);
    return std::unique_copy(__first, __last, __first);
}</PRE></BLOCKQUOTE>

<P>I believe that this much is not controversial. It is only when the
called function is not intended to be an implementation detail, but a
"point of customization" for the algorithm that opinions differ. Of
course opinions also vary on which functions should have what points
of customization.</P>

<P>For purposes of discussion, temporarily assume that
std::swap_ranges intends that the fact that it calls swap be part of
its public interface, and a point of customization. When the ranges
contain scalars, clearly std::swap must be called. When the ranges
contain user-defined types, it is desirable that if the type has a
customized (overloaded) swap function, that it be called instead of
the general std::swap. The reason it is desirable is because of
efficiency. A heavy weight class may be able to "swap" much faster
than performing a series of copy construction and copy assignments on
the entire object.</P>

<H2>The Example</H2>

<P>Consider a user-defined class N::A. And to make it interesting,
this user-defined class is actually a template class:</P>

<BLOCKQUOTE><PRE>namespace N {
template &lt;typename T&gt; class A {/* ... */};
}</PRE></BLOCKQUOTE>

<P>This class A is a "heavy" class that could benefit from an
overloaded swap function (just as the std::containers do). And client
code wants to call swap_ranges on a container of A:</P>

<BLOCKQUOTE><PRE>template &lt;typename T&gt;
void f()
{
   N::A&lt;T&gt; v1[5], v2[5];
   ...
   std::swap_ranges(v1, v1+5, v2);
}</PRE></BLOCKQUOTE>

<P>Now there are two closely related questions to ask:</P>

<OL>
   <LI>How does the author of A&lt;T&gt; write swap so that
   std::swap_ranges will pick it up?
   
   <LI>How does std::swap_ranges call swap so that "customized" code
   will be picked up?
</OL>

<H2>The Solution</H2>

<P>This paper proposes that the answer to the above two questions be:
</P>

<BLOCKQUOTE><P>1. The author of A&lt;T&gt; writes swap within A's
namespace (N) in accordance to what Herb Sutter has termed the
<A HREF="http://www.gotw.ca/publications/mill08.htm">"Interface
Principle"</A></P>

<BLOCKQUOTE><PRE>namespace N
{
&nbsp;
template &lt;typename T&gt; class A {/* ... */};
&nbsp;
template &lt;typename T&gt;
void swap(A&lt;T&gt;&amp; x, A&lt;T&gt;&amp; y)
     {x.swap(y);}
&nbsp;
}  // N</PRE></BLOCKQUOTE>

<P>2. std::swap_ranges calls swap unqualifed so as to enable Koenig
lookup. If argument dependent lookup does not find a better overload,
the general std::swap is used.</P></BLOCKQUOTE>

<P>Having the author of A&lt;T&gt; put the overloaded swap into
namespace std is not currently an option as 17.4.3.1 forbids it.
Section 17.4.3.1 could be modified to allow overloaded definitions,
but further complicatons arise. Consider:</P>

<BLOCKQUOTE><PRE>namespace std
{
&nbsp;
template &lt;class T&gt;
void
swap(T&amp; a, T&amp; b)
{
    T tmp(a);
    a = b;
    b = tmp;
}
&nbsp;
template &lt;class ForwardIterator1, class ForwardIterator2&gt;
ForwardIterator2
swap_ranges(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2)
{
    for (; first1 != last1; ++first1, ++first2)
        std::swap(*first1, *first2);
    return first2;
}
&nbsp;
}  // std
&nbsp;
namespace N
{
&nbsp;
template &lt;typename T&gt; class A {/* ... */};
&nbsp;
}  // N
&nbsp;
namespace std
{
&nbsp;
template &lt;typename T&gt;
void swap(N::A&lt;T&gt;&amp; x, N::A&lt;T&gt;&amp; y)
     {x.swap(y);}
&nbsp;
}  // std</PRE></BLOCKQUOTE>

<P>In this example, swap_ranges will use the general purpose
std::swap, not the overloaded swap for A&lt;T&gt;. Qualified calls
are bound at template definition time. This code could work as
intended if A and swap(A,A) appeared before std::swap_ranges in the
translation unit, but this dependence on ordering is fragile at best.
</P>

<H2>Customization Points</H2>

<P>If templated code is going to allow Koenig lookup on an internal
call, that fact becomes part of the template's interface. It must be
clearly and unambiguously documented. The current standard is
defective in this respect. In this section I attempt a list of all
such places in the standard:</P>

<P><B>25.2.2/7:</B> iter_swap</P>

<BLOCKQUOTE><P>-7- Effects: Calls swap (unqualified) with the values
pointed to by the two iterators a and b.</P></BLOCKQUOTE>

<P><B>25.2.2/3:</B> swap_ranges</P>

<BLOCKQUOTE><P>-3- Effects: For each non-negative integer n &lt;
(last1 - first1) performs: (unqualified) swap(*(first1 + n), *(first2
+ n)).</P></BLOCKQUOTE>

<P><B>25.2.9/1:</B> reverse</P>

<BLOCKQUOTE><P>-1- Effects: For each non-negative integer i &lt;=
(last - first)/2, applies (unqualified) swap to all pairs of the
values referenced by the iterators first + i, (last - i) - 1.</P>
</BLOCKQUOTE>

<P><B>25.2.10/4:</B> rotate</P>

<BLOCKQUOTE><P>-4- Complexity: At most last - first unqualified calls
to swap.</P></BLOCKQUOTE>

<P><B>25.2.11/2:</B> random_shuffle</P>

<BLOCKQUOTE><P>-2- Complexity: Exactly (last - first) - 1 unqualified
calls to swap.</P></BLOCKQUOTE>

<P><B>25.2.12/3:</B> partition</P>

<BLOCKQUOTE><P>-3- Complexity: At most (last - first)/2 unqualifed
calls to swap. Exactly last - first applications of the predicate are
done.</P></BLOCKQUOTE>

<P><B>25.2.12/6:</B> stable_partition</P>

<BLOCKQUOTE><P>-6- Complexity: At most (last - first) * log(last -
first) unqualified calls to swap, but only linear number of swaps if
there is enough extra memory. Exactly last - first applications of
the predicate.</P></BLOCKQUOTE>

<P><B>26.3.3.3/1:</B> valarray transcendentals</P>

<BLOCKQUOTE><P>-1- Each of these functions may only be instantiated
for a type T to which a unique function with the indicated name can
be applied (unqualified). This function shall return a value which is
of type T or which can be unambiguously converted to type T .</P>
</BLOCKQUOTE>
</BODY>
</HTML>
