Re: Use of swap in the standard library
Am 04.12.2010 21:19, schrieb Nikolay Ivchenkov:
On 27 Nov, 22:14, Daniel Kru"gler<daniel.krueg...@googlemail.com>
wrote:
Am 27.11.2010 01:21, schrieb Nikolay Ivchenkov:
Now let's look at iter_swap specification:
template<
class ForwardIterator1,
class ForwardIterator2>
void iter_swap(
ForwardIterator1 a,
ForwardIterator2 b);
Effects: swap(*a, *b).
Requires: a and b shall be dereferenceable. *a shall be
swappable with (20.2.2) *b.
If in the call swap(*a, *b) name 'swap' is supposed to be unqualified
(considering the intention of the "Requires" paragraph), where it is
explicitly stated?
This is a consequence of the definition of the Swappable requirements
which define the context in which a binary non-member swap function shall be
called combined with the allowance specified in above mentioned
[global.functions]/4.
What normative wording states that "Requires" paragraph determines the
meaning of "Effects" paragraph?
I think this is an indirect consequence of how the library introduction describes requirements as of [structure.requirements]/4:
"Requirements are stated in terms of well-defined expressions that define valid terms of the types that satisfy the requirements. For every set of well-defined expression requirements there is a table that specifies an initial set of the valid expressions and their semantics. Any generic algorithm (Clause 25) that uses the well-defined expression requirements is described in terms of the valid expressions for its formal type parameters."
A requirement definition - as the different swappable requirements defined in [swappable.requirements] - defines a contract for both the provider and the caller. The Swappable requirements define a *required* context in which swap shall be called. Obviously iter_swap and other components have to obey the contract if the corresponding swappable requirement is imposed - I don't see how this could be misinterpreted.
2)
20.2.2/1 contains:
"This subclause provides definitions for swappable types and
expressions."
I don't see any definition for swappable types, though there is
definition of ValueSwappable types.
This is an introductory sentence. It doesn't allow for far-reaching
interpretations.
This is a potential readability issue. A reader may expect that the
subclause introduces a definition for "swappable types". Absence of
such definition may be surprising for him/her.
OK, but this is again a pure editorial thing. Again, you could try to suggest editorial improvements to Pete Becker. I see no real defect here that would justify an LWG issue, but if you have a different opinion on that you are free to post such a library issue description to
<lwgchair$at$gmail$dot$com>.
20.2.2/4 contains:
"An rvalue or lvalue t is swappable if and only if t is swappable
with any rvalue or lvalue, respectively, of type T"
Some lvalues and rvalues do not refer to objects. For example, there
are no expressions that can be swappable with lvalue *(int*)0.
Such expressions can occur in code that does not potentially evaluate
operands, as in decltype expressions.
This is unrelated to swappable requirements (due to presence of
required effects). My point is that NOT _any_ rvalue or lvalue of type
T should be under consideration. Otherwise, what should the quoted
sentence mean?
OK, I understand now what you mean here. I agree that this description /can/ be misunderstood. It can be read (but was not intended this way) that one part of the swappable requirements is that it imposes that the swap function selected in the described context shall be free of preconditions. I'm open for wording improvements to make the intentions clearer: P. 2 defines that X is swappable with Y without specifying the concrete value category of X and Y. P. 4 is defining more specifically what the term "an lvalue/rvalue [of type T] is swappable" means as refinement of the former more general definition. In concept language the former version corresponds to HasSwap<U, T>, while the latter
"an rvalue/lvalue of type T is swappable" would correspond to HasSwap<T&&, T&&> or HasSwap<T&, T&>, respectively for object types T and U.
I would say, that above suggestion is at least controversial in
some aspects as mentioned above. What if someone had intended to have a private
swap function but a public non-member swap function? The tentative decision to
make SFINAE contexts access-aware is extremely new and did not exist
But now we have std::is_constructible, which must check accessibility.
Sure, but keep in mind that std::is_constructible has been specified very shortly before the Swappable requirements and just the fact that a corresponding component has been standardized, does not necessarily imply that it has been fully implemented. In fact, that would be an ideal situation, but compiler built-ins are somewhat special in this situation. Before they are available, they are typically simulated by normal language /library means (and those simulations had been implemented before the proposal). Compiler-built-ins become standardized if general agreement exists that they would be implementable. Further-on the requirements on is_constructible have been relaxed to be restricted to the immediate context.
So, there would be not much of a difference to ask for a new compiler-built-in is_member_swappable. Such an addition would have been very controversial given that normal ADL-lookup-enabled swap contexts are cheap and easy to realize without such efforts.
Last but not
least the general Swappable concepts are intentionally written to support
mixed-type swaps, which is not possible with the exchange template as currently
defined (see A and Proxy above).
I think that it is possible to create more general definition (though,
it will be complicated). Moreover, an implementation may provide a
definition for such template by use of special compiler support. If
the committee considers this as too difficult and unacceptable, I can
propose another approach: when a call swap(t, u) would be ambiguous
due to consideration of std::swap, std::swap should be removed from
consideration.
namespace std
{
template<class _T1, class _T2>
auto call_swap(_T1&&__t1, _T2&&__t2) ->
decltype(swap(
std::forward<_T1>(__t1),
std::forward<_T2>(__t2)))
{
return swap(
std::forward<_T1>(__t1),
std::forward<_T2>(__t2));
}
struct __test_swap
{
template<class _T1, class _T2>
__test_swap(
_T1&&__t1,
_T2&&__t2,
decltype((void)swap(
std::forward<_T1>(__t1),
std::forward<_T2>(__t2))) * = 0)
{}
};
namespace __call_swap
{
void swap(); // hides std::swap
template<class _T1, class _T2>
auto call_swap(_T1&&__t1, _T2&&__t2) ->
decltype(swap(
std::forward<typename enable_if<
!is_constructible<
__test_swap,
_T1,
_T2>::value, _T1>::type>(__t1),
std::forward<_T2>(__t2)))
{
return swap(
std::forward<_T1>(__t1),
std::forward<_T2>(__t2));
}
}
using __call_swap::call_swap;
} // namespace std
Rationale: if we have a set of classes, for which swap functions are
provided only as member functions, we could define (in the respective
namespaces) a non-member function template "swap", which can call
appropriate member functions.
This is surely a great prototype for a new component named call_swap with a lot of advantages. But the wording provided as part of the
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3048.html
proposal was to fix an existing and well-known requirement "Swappable". You are suggesting that this should be done by providing a library component std::call_swap that does all the required stuff *and* something more. But this means that basically all library code referring to swap would be required to be replaced by call_swap and existing libraries - which already do use proper ADL-aware swap are basically non-conforming.
Don't misunderstand my criticism: I think your approach is very cute but it is nothing that could be applied in a last-minute fix of the current draft. I think that - depending on how the language evolves - your suggestion is the right way for C++1x.
HTH & Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]