Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Use of swap in the standard library

25 views
Skip to first unread message

Nikolay Ivchenkov

unread,
Nov 26, 2010, 7:21:27 PM11/26/10
to

I see several potential issues with certain parts of the standard
library specification related to swap functions.

1)
According to 17.6.1.1/3,

"Whenever a name x defined in the standard library is mentioned, the
name x is assumed to be fully qualified
as ::std::x, unless explicitly described otherwise. For example, if
the Effects section for library function F
is described as calling library function G, the function ::std::G is
meant".

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? Moreover, I don't see where the description of
std::iter_swap explicitly requires to provide declarations of
std::swap from <utility> header before the invocation swap(*a, *b).

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.

20.2.2/2 contains:

"An object t is swappable with an object u if and only if:
- the expressions swap(t, u) and swap(u, t) are valid when evaluated
in the context described below, and [...]"

AFAICS, t and u should be expressions referring to objects, not just
objects.

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. A
glvalue of cv-unqualified type T can refer to an object of type 'const
T' (cv-qualifier can be discarded by const_cast). The wording should
be more precise with respect to considered kinds of lvalues and
rvalues.

3)
I don't understand why class member functions with name 'swap' are not
considered for the purposes of defining swappable expressions. If our
class provides appropriate member function, why should we define
additional non-member function with the same semantics? I would prefer
to use some exchange function that works like this:

-----------------------------------------------------

#include <type_traits>
#include <utility>

template <class T>
struct has_member_swap_impl
{
template <class U, class B, class R>
static typename std::enable_if
<!std::is_volatile<U>::value>::type
test(R (B::*)(U &));

template <class U, class B, class R>
static typename std::enable_if
<!std::is_volatile<U>::value>::type
test(R (B::*)(U &) &);

template <class U, class B, class R>
static typename std::enable_if
<std::is_volatile<U>::value>::type
test(R (B::*)(U &) volatile);

template <class U, class B, class R>
static typename std::enable_if
<std::is_volatile<U>::value>::type
test(R (B::*)(U &) volatile &);

template <class U>
struct Identity {};

struct HasSwap
{
template
<
class U,
class = decltype(test<U>(&U::swap))
>
HasSwap(Identity<U>) {}
};

static bool const value =
std::is_constructible
<
HasSwap,
Identity<T>
> ::value;
};

template <class T>
struct has_member_swap :
std::integral_constant
<
bool,
has_member_swap_impl<T>::value
>
{};

template <class T>
void exchange_impl(T &t1, T &t2, std::true_type)
{
t1.swap(t2);
}

template <class T>
void exchange_impl(T &t1, T &t2, std::false_type)
{
using std::swap;
swap(t1, t2);
}

#define STATIC_ASSERT(expr) static_assert(expr, #expr)

template <class T>
void exchange(T &t1, T &t2)
{
STATIC_ASSERT(!std::is_const<T>::value);
(exchange_impl)(t1, t2, has_member_swap<T>());
}

-----------------------------------------------------

If I didn't miss something, ::exchange(t, u) attempts to call
t.swap(u) iff:
- t and u are lvalues of the same type cv T (where cv is volatile or
empty cv-qualification), and
- T is a class type which has public non-static non-template member
function "swap", whose name in the call t.swap(u) can be unambiguously
found (see 10.2), and this function has single explicit parameter of
type cv T & and implicit object parameter of type cv B & (where B is T
or its base class type), but does not have any other parameters or
ellipsis, and
- T has no any member function template with name "swap" that can be
found during name lookup in t.swap(u) (this is consequence of possible
core language defect).

Otherwise, if the first condition is satisfied, ::exchange(t, u)
follows currently approved ADL-based approach.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Daniel Krügler

unread,
Nov 27, 2010, 2:14:34 PM11/27/10
to
Am 27.11.2010 01:21, schrieb Nikolay Ivchenkov:
>
> I see several potential issues with certain parts of the standard
> library specification related to swap functions.
>
> 1)
> According to 17.6.1.1/3,
>
> "Whenever a name x defined in the standard library is mentioned, the
> name x is assumed to be fully qualified
> as ::std::x, unless explicitly described otherwise. For example, if
> the Effects section for library function F
> is described as calling library function G, the function ::std::G is
> meant".

Also note that according to 17.6.4.4 [global.functions]/4:

"Unless otherwise specified, global and non-member functions in the standard
library shall not use functions from another namespace which are found through
argument-dependent name lookup (3.4.2). [ Note: The
phrase “unless otherwise specified” is intended to allow argument-dependent
lookup in cases like that of ostream_iterators: Effects:

*out_stream << value;
if (delim != 0)
*out_stream << delim;
return (*this);

—end note ]

17.6.1.1 is about library contents in general, and 17.6.4.4 specifically
concentrates on some non-member functions that have granted special allowance to
do so. Generally, a more specific requirement "overrides" a more general
requirement.

For exactly this reason it makes normative difference, if the library would omit
the std:: in front of move and forward calls, which are non-member functions. It
is therefore no *superfluous* wording, when [allocator.requirements]/2 says:

"Within Tables 41 and 42, the use of move and forward
always refers to std::move and std::forward, respectively."

In all other places of the library specification move and forward must
specifically called as std::move and std::forward, if these functions are
intended to be called instead of possible overloads provided by the user.

> 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.

> Moreover, I don't see where the description of
> std::iter_swap explicitly requires to provide declarations of
> std::swap from<utility> header before the invocation swap(*a, *b).
>
> 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. The swappable requirements of 20.2.2 can be participated into
several groups of different generality. They more or less correspond to the
previous concepts HasSwap<T, U> and Swappable<T> plus a new ValueSwappable
requirement set. The definitions refer to types which is indirectly a
consequence of the introductory definition of types T and U and later referring
to them. The specialization defined in p. 4

"An rvalue or lvalue t is swappable if and only if t is swappable with any

rvalue or lvalue, respectively, of type T."

is also referring to a type T. Since the term swappable is overloaded here, it
does not really help to say that a type T is swappable, because that does not
answer everything necessary. The ValueSwappable requirements were introduced
because the collection of requirements of this refining requirement set was
rather often occurring in the library.

The wording was specifically written to support proxy types which may provide
the following overload set:

struct A { /../ };
struct Proxy { /../ };

void swap(A&, A&);
void swap(Proxy, A&);
void swap(A&, Proxy);

> 20.2.2/2 contains:
>
> "An object t is swappable with an object u if and only if:
> - the expressions swap(t, u) and swap(u, t) are valid when evaluated
> in the context described below, and [...]"
>
> AFAICS, t and u should be expressions referring to objects, not just
> objects.

Please quote the complete part, this meaning is implied. Bullet 2 says:

— these expressions have the following effects:
— the object referred to by t has the value originally held by u and
— the object referred to by u has the value originally held by t.


> 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 does not potentially evaluate
operands, as in decltype expressions.

> A glvalue of cv-unqualified type T can refer to an object of type 'const
> T' (cv-qualifier can be discarded by const_cast). The wording should
> be more precise with respect to considered kinds of lvalues and
> rvalues.

Currently I see now reason how this could lead to problems. The definition of
/swappable/ does not invalidate core language rules or other rules that would
lead to undefined behaviour.

Note also that there exist rare but legal use-cases of swapping a const object,
especially if that is an proxy object. Similar rare but sometimes useful as
evaluating

std::is_assignable<const T&, const T&>::value

for proxies that occur in unusual but known assignment situations.

> 3)
> I don't understand why class member functions with name 'swap' are not
> considered for the purposes of defining swappable expressions.

It was considered as too late: Concepts had been removed. They would have
allowed to take advantage of a concept maps to "equalize" both member and
non-member functions. If you would have added this requirement that would mean
that the library would have needed to provide a template tool that would do all
the stuff necessary to lead to the same form of expression for usage of member
swap or non-member swap and everyone would be required to use this tool to
provide the proper context of a swap operation based on swappable requirements.

Requirement tables are based on valid expressions and it is not reasonable
requirement that such a requirement is:

"either the expression a.swap(b) or the expression swap(a, b) is valid".

Generic code must *know* what kind of expression has to be used, so above
fictive requirement would be practically use-less.

> If our
> class provides appropriate member function, why should we define
> additional non-member function with the same semantics? I would prefer
> to use some exchange function that works like this:
>
> -----------------------------------------------------
>
> #include<type_traits>
> #include<utility>
>
> template<class T>
> struct has_member_swap_impl
> {
> template<class U, class B, class R>
> static typename std::enable_if
> <!std::is_volatile<U>::value>::type
> test(R (B::*)(U&));
>
> template<class U, class B, class R>
> static typename std::enable_if
> <!std::is_volatile<U>::value>::type

> test(R (B::*)(U&)&);


>
> template<class U, class B, class R>
> static typename std::enable_if
> <std::is_volatile<U>::value>::type

> test(R (B::*)(U&) volatile);


>
> template<class U, class B, class R>
> static typename std::enable_if
> <std::is_volatile<U>::value>::type
> test(R (B::*)(U&) volatile&);
>
> template<class U>
> struct Identity {};
>
> struct HasSwap
> {
> template
> <
> class U,
> class = decltype(test<U>(&U::swap))
>>
> HasSwap(Identity<U>) {}
> };
>
> static bool const value =
> std::is_constructible
> <
> HasSwap,
> Identity<T>
>> ::value;
> };
>
> template<class T>
> struct has_member_swap :
> std::integral_constant
> <
> bool,
> has_member_swap_impl<T>::value
>>
> {};
>
> template<class T>

> void exchange_impl(T&t1, T&t2, std::true_type)
> {
> t1.swap(t2);
> }
>
> template<class T>
> void exchange_impl(T&t1, T&t2, std::false_type)


> {
> using std::swap;
> swap(t1, t2);
> }
>
> #define STATIC_ASSERT(expr) static_assert(expr, #expr)
>
> template<class T>
> void exchange(T&t1, T&t2)
> {
> STATIC_ASSERT(!std::is_const<T>::value);
> (exchange_impl)(t1, t2, has_member_swap<T>());
> }

Sure, that is the library tool I was referring to. But it was late in the game
after concept removal. No-one wanted to risk the invention of a tool that has
such a fundamental impact and little time to be tested in all situations. E.g.
the constraint on non-const arguments is intentionally not imposed on the
current swappable requirements.

> -----------------------------------------------------
>
> If I didn't miss something, ::exchange(t, u) attempts to call
> t.swap(u) iff:
> - t and u are lvalues of the same type cv T (where cv is volatile or
> empty cv-qualification), and
> - T is a class type which has public non-static non-template member
> function "swap", whose name in the call t.swap(u) can be unambiguously
> found (see 10.2), and this function has single explicit parameter of

> type cv T& and implicit object parameter of type cv B& (where B is T


> or its base class type), but does not have any other parameters or
> ellipsis, and
> - T has no any member function template with name "swap" that can be
> found during name lookup in t.swap(u) (this is consequence of possible
> core language defect).
>
> Otherwise, if the first condition is satisfied, ::exchange(t, u)
> follows currently approved ADL-based approach.

It is a nice idea, but it was much too late to invent something like this after
concept removal. 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, when the
Swappable requirements where introduced in the library. This would lead to
different behaviour. I don't want to say that this might not be a good idea, but
it requires more than just an one thought to make such a decision. 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).

HTH & Greetings from Bremen,

Daniel Krügler

Gene Bushuyev

unread,
Nov 30, 2010, 9:58:47 PM11/30/10
to
On Nov 26, 7:21 pm, Nikolay Ivchenkov <ts...@mail.ru> wrote:
> 3)
> I don't understand why class member functions with name 'swap' are not
> considered for the purposes of defining swappable expressions. If our
> class provides appropriate member function, why should we define
> additional non-member function with the same semantics?

It would be non-trivial for algorithms (if possible at all) to figure
out whether member function should be used.
I think the fundamental problem with swap is that C++ doesn't
recognize it as a primitive operation that requires its own operator,
which behavior should be defined for built-in and user-defined types,
including default swap constructor and assigment. If we had something
like "operator <=>" in the language we would have the same treatment
of free standing and member operator and many complexities and
inconsistencies would have automatically disappeared.
As a side note, I believe it's trivial for hardware to implement swap
for built-in types with the same or better complexity than that of
assignment, if swap-operator were ever defined.

Nikolay Ivchenkov

unread,
Dec 4, 2010, 3:19:31 PM12/4/10
to
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?

> > 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.

> > 20.2.2/2 contains:
>
> > "An object t is swappable with an object u if and only if:
> > - the expressions swap(t, u) and swap(u, t) are valid when evaluated
> > in the context described below, and [...]"
>
> > AFAICS, t and u should be expressions referring to objects, not just
> > objects.
>
> Please quote the complete part, this meaning is implied.

This is also a potential readability issue. Terms "object" and
"expression" have different meaning. I don't understand why the term
"object" should be used where "expression" is meant.

> > 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?

> 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.

> 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.

Daniel Krügler

unread,
Dec 11, 2010, 8:49:52 PM12/11/10
to

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


Nikolay Ivchenkov

unread,
Dec 13, 2010, 6:33:58 AM12/13/10
to

On 12 Dec, 04:49, Daniel Kru"gler <daniel.krueg...@googlemail.com>
wrote:

> Am 04.12.2010 21:19, schrieb Nikolay Ivchenkov:
>
> > 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:

It seems that [structure.requirements] is not normative (subclause
[description] has status "informative").

> >>> 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.

May be the definition of ValueSwappable types should be completely
removed (see below).

> >>> 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 don't understand what "swappable expression" should mean. Let's
consider the following example:

#include <algorithm>
#include <iostream>
#include <iterator>

int main()
{
int *arr[2] = {};

{
int i;
arr[1] = &i;
}

std::reverse(std::begin(arr), std::end(arr));

std::cout << arr[1] << std::endl;
}

When std::reverse is called, arr[0] holds the null pointer value and
arr[1] holds an invalid pointer value (Note: effect of using an
invalid pointer value is undefined - see N3225 - 3.7.4.2/4). Now let's
consider the following questions:

1) Is arr[0] swappable with arr[1]?
2) Is arr[0] swappable?
3) Is arr[1] swappable?
4) Is the type int** ValueSwappable?

According to my interpretation of 20.2.2/2, 20.2.2/4, and 20.2.2/5:

* the answer for the 1-st question is "no", because there is no
guarantee that swap(arr[0], arr[1]) and swap(arr[1], arr[0]) will
produce the required effects - when arr[0] will hold an invalid
pointer value and arr[1] will hold the null pointer value;

* the answer for the 2-nd question is "no", because arr[0] is not
"swappable with any lvalue of type int*" (for example, it is not
swappable with arr[1]).

* the answer for the 3-rd question is "no", because there is no
expression E such that arr[1] would be swappable with E;

* the answer for the 4-th question is "no", because, for example, (arr
+ 1) is dereferenceable, but *(arr + 1) is not swappable.

If in the definition of "swappable expression" required effects should
be ignored, then the answer for the 2-nd, 3-rd, and 4-th questions is
"yes", however, evaluation of std::reverse(std::begin(arr),
std::end(arr)) presumably leads to undefined behavior in spite of the
fact that the precondition for the use of std::reverse is satisfied
(expression *first is swappable).

I can propose the following resolution:

1) remove the definition of swappable expression (and keep only
"swappable with"),
2) remove the definition of ValueSwappable type,
3) change "Requres" paragraph for std::reverse as described below:

Requires: For each non-negative integer i < distance(first, last)/2,
*next(first, i) shall be swappable with *prev(prev(last, i)).

4) change the description of other library components where it is
necessary.

> 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.

OK

Daniel Krügler

unread,
Dec 13, 2010, 12:07:03 PM12/13/10
to
Am 13.12.2010 12:33, schrieb Nikolay Ivchenkov:
>
> On 12 Dec, 04:49, Daniel Kru"gler<daniel.krueg...@googlemail.com>
> wrote:
>> Am 04.12.2010 21:19, schrieb Nikolay Ivchenkov:
>>
>>> 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:
>
> It seems that [structure.requirements] is not normative (subclause
> [description] has status "informative").

I think this conclusion is too strong, otherwise several *obviously* normative
text part of this sub-clause or it's sub-clauses would also be informative,
which does not make much sense. I'm asking the project editor whether it is
possible to make that informative reduction weaker. If not, there is maybe a
library issue lurking around, but I'm not sure.

> May be the definition of ValueSwappable types should be completely
> removed (see below).

It was invented to simply wording. I doubt that it's removal will be accepted.
See also below.

> I don't understand what "swappable expression" should mean. Let's
> consider the following example:
>
> #include<algorithm>
> #include<iostream>
> #include<iterator>
>
> int main()
> {
> int *arr[2] = {};
>
> {
> int i;
> arr[1] =&i;
> }
>
> std::reverse(std::begin(arr), std::end(arr));
>
> std::cout<< arr[1]<< std::endl;
> }
>
> When std::reverse is called, arr[0] holds the null pointer value and
> arr[1] holds an invalid pointer value (Note: effect of using an
> invalid pointer value is undefined - see N3225 - 3.7.4.2/4).

At this point we need to halt, because [swappable.requirements] p. 2 . b. 1
already requires that the "expressions swap(t, u) and swap(u, t) are valid when
evaluated in the context described below". This is a basic requirement that you
will find in all requirement tables/descriptions. Anything that violates the
basic requirements cannot be considered to satisfy the requirement set as a
whole thing.

> Now let's
> consider the following questions:
>
> 1) Is arr[0] swappable with arr[1]?

It cannot be, because the expression swap(arr[0], arr[1]) "is not a valid when
evaluated".

> 2) Is arr[0] swappable?

No, see above.

> 3) Is arr[1] swappable?

Nope as well.

> 4) Is the type int** ValueSwappable?

The type int** is ValueSwappable, yes. (It does not matter whether some
particular values don't satisfy the basic-requirements. This prevents some
particular values to be swappable, but not the type as a whole).

> According to my interpretation of 20.2.2/2, 20.2.2/4, and 20.2.2/5:

[..]

> * the answer for the 4-th question is "no", because, for example, (arr
> + 1) is dereferenceable, but *(arr + 1) is not swappable.

It is irrelevant, whether a particular subset of values is not swappable,
because for this particular subset of values the preconditions of a well-formed
and well-defined expression is not satisfied. The normative wording could be
clearer here, but this nothing else but the pre-conditions of a potentially
evaluated, valid expression.

Keep in mind that requirement tables and the language concepts are requirements
that are assumed to be valid for the set of values that can potentially be
evaluated within some function/algorithm. We can introduce the concept
LessThanComparable<T>, even though this concept applies only to a subset of all
values of T (the domain on which the concept functions can be applied). The
iterator requirement make this in particular clear: 24.2.3 says that a type
satisfying the iterator requirements shall also satisfy the EqualityComparable
requirements. Nevertheless p. emphasizes:

"In Table 107, the term the domain of == is used in the ordinary mathematical
sense to denote the set of values over which == is (required to be) defined.
This set can change over time."

Even though this extra mentioned for iterators (because several operations are
in general not total), this applies for other operations and other requirements
as well.

> If in the definition of "swappable expression" required effects should
> be ignored, then the answer for the 2-nd, 3-rd, and 4-th questions is
> "yes", however, evaluation of std::reverse(std::begin(arr),
> std::end(arr)) presumably leads to undefined behavior in spite of the
> fact that the precondition for the use of std::reverse is satisfied
> (expression *first is swappable).
>
> I can propose the following resolution:
>
> 1) remove the definition of swappable expression (and keep only
> "swappable with"),

Why? This term just shortcuts a special case of swappable-with.
It is already used at several places, e.g. [nullablepointer.requirements] p. 1 b. 2:

"lvalues of type P are swappable (20.2.2),"

or [unord.hash] p. 1 b. 2:

"be swappable (20.2.2) for lvalues,"

or 20.9.9.2.5 p. 6:

"get_deleter() shall be swappable (20.2.2)"

or [alg.reverse] p. 2:

"*first shall be swappable (20.2.2)."

etc. This short-cut says exactly the same as if I would write that
"With lv1 and lv2 being lvalues of type T, lv1 shall be swappable with lv2" for
lvalues or "With rv1 and rv2 being rvalues of type T, rv1 shall be swappable
with rv2". We don't want to repeat these lengthy sentences but introduce the
short cut "lv is swappable", or "rv is swappable", instead. The requirement that
the arguments must be valid, is already part of the basic requirements. It is
also part of the basic library requirements, as mentioned in 17.6.3.9 Function
arguments [res.on.arguments], in particular for p. 1 b 1 (for library functions)
and [res.on.functions] p. 2 b. 3.

> 2) remove the definition of ValueSwappable type,

That does not make sense to me, it is already used. It was invented, because
during the introduction of the swappable requirements this situation occurred
quite often.

> 3) change "Requres" paragraph for std::reverse as described below:
>
> Requires: For each non-negative integer i< distance(first, last)/2,
> *next(first, i) shall be swappable with *prev(prev(last, i)).

The short-cuts where introduced to prevent this. They have the same meaning. See
also next point.

> 4) change the description of other library components where it is
> necessary.

You could say that the library requirements and effects are sometimes quite
inconsistent and sloppy, because they don't look the same. I agree with that.
One of the great advantages of concept requirements was to get rid of these
inconsistencies. But with the removal of language concepts for C++0x no-one was
willing to do the "big clean sweep". Unless you find implementations that get
the specification wrong given these requirement sets I don't expect that anyone
will fix them. Note also that it would not make much sense to start with the
swappable requirements. Especially in the algorithm requirements you'll find
several such funny looking specifications. E.g. [alg.replace] p.1 requires:

"The expression *first = new_value shall be valid."

Reading strictly it could be interpreted that this requirement would be imposed
on this exactly this particular initial expression. Does that meant we require
it even for the empty range? Does it mean that we don't require it for the
second, third, or n-th iterator value i of a non-empty sequence? No, it does not
in either case, of-course. This is basically the same form of expression
validity as has been reused for the swappable requirements as well. *first is
just the placeholder describing the expression for all other left-hand arguments
that will be applied within the function.

HTH & Greetings from Bremen,

Daniel Krügler


Nikolay Ivchenkov

unread,
Dec 14, 2010, 2:52:38 PM12/14/10
to

On 13 Dec, 20:07, Daniel Kru"gler <daniel.krueg...@googlemail.com>
wrote:

Are you sure? What can you say about the evaluation of
std::reverse(arr + 0, arr + 1)? And what can you say about the
evaluation of std::reverse(a + 0, a + 2) in the example below?

#include <algorithm>
#include <iterator>

int main()
{
int *a[3] = {};

{
int i;
a[2] = &i;
}

// is this well-defined?
std::reverse(a + 0, a + 2);

// leads to undefined behavior
std::reverse(a + 0, a + 3);
}

>> 3) Is arr[1] swappable?
>
> Nope as well.
>
>> 4) Is the type int** ValueSwappable?
>
> The type int** is ValueSwappable, yes. (It does not matter whether some
> particular values don't satisfy the basic-requirements. This prevents some
> particular values to be swappable, but not the type as a whole).

What exactly

A type X satisfying any of the iterator requirements (24.2) is
ValueSwappable if, for any dereferenceable object x of type X, *x is
swappable.

is supposed to mean:

1) A type X satisfying any of the iterator requirements (24.2) is
ValueSwappable if _there can exist_ a dereferenceable object x of type
X _such that_ *x _would be_ swappable.

or

2) A type X satisfying any of the iterator requirements (24.2) is
ValueSwappable if for _every_ dereferenceable object x of type X, *x
is swappable.

3) something else?

>> If in the definition of "swappable expression" required effects should
>> be ignored, then the answer for the 2-nd, 3-rd, and 4-th questions is
>> "yes", however, evaluation of std::reverse(std::begin(arr),
>> std::end(arr)) presumably leads to undefined behavior in spite of the
>> fact that the precondition for the use of std::reverse is satisfied
>> (expression *first is swappable).
>
>> I can propose the following resolution:
>
>> 1) remove the definition of swappable expression (and keep only
>> "swappable with"),
>
> Why? This term just shortcuts a special case of swappable-with.

I don't see any _correct_ rationale for presence of such definitions.
In general, for a given expression t, there are 2 subsets of
expressions with the same type and value category (lvalue/rvalue):
1) subset of expressions which are swappable with t and
2) subset of expressions which are not swappable with t.

Both subsets may be non-empty. For example, in the sample above a[0]
is swappable with a[1], but a[0] is not swappable with a[2]. If, for a
library component, the preconditions are defined in terms of simply
swappable (not "swappable with") expressions or ValueSwappable types,
we have either:
1) unreasonable violation of preconditions - when the 2-nd subset is
required to be empty (e.g., in std::reverse(a + 0, a + 2)), or
2) undefined behavior of the library component having its
preconditions satisfied - when the 2-nd subset is not required to be
empty (e.g. in std::reverse(a + 0, a + 3)).

The definition of "swappable with" consists of two parts:
1) description of well-formed expressions and
2) description of required effects.

A definition for ValueSwappable types can be introduced only in terms
of well-formed expressions. Required effects are meaningful only with
regard to concrete pair of expressions, so required effects shall not
be a part of a type classification. The same conclusions can be
applied to abstract lvalues or rvalues of some types.

void f(int **i, int **j)
{
std::swap(*i, *j);
}

Even if we know that i and j are dereferenceable, we can't say whether
*i is swappable with *j without additional knowledge about passed
arguments.

> It is already used at several places, e.g. [nullablepointer.requirements] p. 1 b. 2:
>
> "lvalues of type P are swappable (20.2.2),"

It seems, the problem is systematic:

A type P meets the requirements of NullablePointer if:
[...]
the expressions shown in Table 41 are valid and have the indicated
semantics

I believe that such method of description is far from being perfect.

> etc. This short-cut says exactly the same as if I would write that
> "With lv1 and lv2 being lvalues of type T, lv1 shall be swappable with lv2" for
> lvalues or "With rv1 and rv2 being rvalues of type T, rv1 shall be swappable
> with rv2".

Both options are incorrect if the semantics shall be under
consideration.

> We don't want to repeat these lengthy sentences

Happily, some descriptions have correct form:

std::swap for arrays:

Requires: a[i] shall be swappable with (20.2.2) b[i] for all i in
the range [0,N).

std::iter_swap:

Requires: a and b shall be dereferenceable. *a shall be swappable
with (20.2.2) *b.

> The requirement that


> the arguments must be valid, is already part of the basic requirements. It is
> also part of the basic library requirements, as mentioned in 17.6.3.9 Function
> arguments [res.on.arguments], in particular for p. 1 b 1 (for library functions)
> and [res.on.functions] p. 2 b. 3.

How this is related to my examples?

>> 3) change "Requres" paragraph for std::reverse as described below:
>
>> Requires: For each non-negative integer i< distance(first, last)/2,
>> *next(first, i) shall be swappable with *prev(prev(last, i)).
>
> The short-cuts where introduced to prevent this. They have the same meaning.

I strongly disagree with that. My wording clearly states that for
every pair of iterators {x, y}, to which std::iter_swap(x, y) is
applied, *x shall be swappable with *y. It imposes no requirements on
any values outside the specified range (to which std::reverse
applies). In particular, the call std::reverse(a + 0, a + 2) in the
example above is well-defined in spite of the fact that a[0] is not
swappable with a[2]. In contrast, use of generalized forms "swappable
expression" and "ValueSwappable type" does not allow to consider
concrete subset of expressions.

> Note also that it would not make much sense to start with the
> swappable requirements. Especially in the algorithm requirements you'll find
> several such funny looking specifications. E.g. [alg.replace] p.1 requires:
>
> "The expression *first = new_value shall be valid."
>
> Reading strictly it could be interpreted that this requirement would be imposed
> on this exactly this particular initial expression. Does that meant we require
> it even for the empty range? Does it mean that we don't require it for the
> second, third, or n-th iterator value i of a non-empty sequence? No, it does not
> in either case, of-course. This is basically the same form of expression
> validity as has been reused for the swappable requirements as well. *first is
> just the placeholder describing the expression for all other left-hand arguments
> that will be applied within the function.

This is clearly incorrect style of description. It would be really
funny to see such specification in an International Standard.

Daniel Krügler

unread,
Dec 16, 2010, 6:20:11 AM12/16/10
to

Yes ;-)

> What can you say about the evaluation of
> std::reverse(arr + 0, arr + 1)? And what can you say about the
> evaluation of std::reverse(a + 0, a + 2) in the example below?

These are both questions different from the original one. The original
example is one, where std::swap(arr[0], arr[1]) *is* evaluated. In this
context the expression is clearly no valid expression.

The library requirement sets have at least two different purposes:

They require that an expression is valid, even if it is not evaluated -
informally I denote that as a "static" requirement (or the
"well-formed"-criterion).

In this case we cannot argue about effects and it is perfectly OK to say
that the expression std::swap(arr[0], arr[1]) is valid. This is
important, because that means that the standard does not impose overly
strict requirements on implementations to prevent those expressions
occurring at all.

The second important requirement of the requirement sets is a "dynamic"
(also my informal notation) or runtime requirement: If the corresponding
expressions are required *or* allowed to be evaluated for given
arguments for a correspondingly constrained function, the expression
must also be well-defined. This requirement is not satisfied in your
original example, but - since there is no actually allowed evaluation of
similarly critical (that would cause undefined behaviour) expressions in
your second example std::reverse(a + 0, a + 2), were there is no
violation of the client contract.

Let me emphasize that this is nothing special about the swappable
requirements. It applies to all other requirements set if they are
imposed on algorithms. I already mentioned the example of an assignment
that is not evaluated in my previous reply.

Returning to you "a-example": The evaluation of

std::reverse(a + 0, a + 2)

is well-formed and well-defined, because the correspondingly evaluated
swap expression satisfies both the static and the dynamic requirements
(well-formed and well-defined). I see no magic here.

> What exactly
>
> A type X satisfying any of the iterator requirements (24.2) is
> ValueSwappable if, for any dereferenceable object x of type X, *x is
> swappable.
>
> is supposed to mean:
>
> 1) A type X satisfying any of the iterator requirements (24.2) is
> ValueSwappable if _there can exist_ a dereferenceable object x of type
> X _such that_ *x _would be_ swappable.

Looks too strict to me. There could be a type satisfying the iterator
requirements where no object of this type could be dereferencable.

Typically examples where these types are valid are degenerate (empty)
ranges. I see no evidence that the standard imposes this strictness for
any other requirement set or language concept.

> or
>
> 2) A type X satisfying any of the iterator requirements (24.2) is
> ValueSwappable if for _every_ dereferenceable object x of type X, *x
> is swappable.

This is surely incorrect. The preconditions of both dereferencing and
swap operation must be satisfied in a particular evaluation context.

> 3) something else?

I already tried to explain it. Here a last attempt to present it
differently: The answer is that there is a static and a dynamic
requirement. If the dynamic requirement is never allowed to be evaluated
as part of some particular algorithm there is no reason why a type does
not satisfy these requirements if the static requirements are satisfied
because there is no way to prove the contrary. This also nicely shows,
that we cannot only speak of type properties but also of runtime
behaviour. This is the reason why referring to expressions (and not only
on types) is important. This also shows that the swappable requirements
are not different from other expression-based requirement sets.

> I don't see any _correct_ rationale for presence of such definitions.

Sorry for that, but that judgment seems to go beyond a normal criticism
on Swappable requirements. This seems to be an overall and general
criticism on expression-based requirement sets. But I believe that we
have to live with them for C++0x.

> In general, for a given expression t, there are 2 subsets of
> expressions with the same type and value category (lvalue/rvalue):
> 1) subset of expressions which are swappable with t and
> 2) subset of expressions which are not swappable with t.
>
> Both subsets may be non-empty. For example, in the sample above a[0]
> is swappable with a[1], but a[0] is not swappable with a[2].

This interpretation exceeds what the standard says. There is no
*specific* requirement that a[0] is swappable. What you are presenting
here is your interpretation of what the standard says. I argue this
interpretation goes too far based on the general way of how the
requirements on algorithms are imposed. Otherwise every algorithm would
need to describe dynamic situations where these requirements are not
necessary. Just one example: In [alg.is_permutation] the requirement exists:

"The comparison function shall be an equivalence relation."

The intention clearly is that of an dynamic requirement. We can easily
construct examples where for some argument values - that are out of the
domain of a specific comparison function - this requirement would not
hold but still this comparison function can be used as valid argument of
such a constrained function, *unless* the dereferenced iterator values
would cause to enter this out-of-domain situation.

> If, for a library component, the preconditions are defined in terms
of simply
> swappable (not "swappable with") expressions or ValueSwappable types,
> we have either:
> 1) unreasonable violation of preconditions - when the 2-nd subset is
> required to be empty (e.g., in std::reverse(a + 0, a + 2)), or
> 2) undefined behavior of the library component having its
> preconditions satisfied - when the 2-nd subset is not required to be
> empty (e.g. in std::reverse(a + 0, a + 3)).

I believe I already answered on this above.

> The definition of "swappable with" consists of two parts:
> 1) description of well-formed expressions and
> 2) description of required effects.

.. in a context where the evaluation is allowed or required, yes.

> A definition for ValueSwappable types can be introduced only in terms
> of well-formed expressions.

Nope. It has also effect requirements, when the corresponding algorithm
requires a corresponding swap call with specific arguments.

> Required effects are meaningful only with
> regard to concrete pair of expressions,

Correct, similar for any other expression-based requirements that is
binary. For n-ary expression based operations, these are relevant for
the actually invoked or allowed to be invoked operation. Again: There is
nothing special in regard to swappable requirements here.

> so required effects shall not be a part of a type classification.

This is a false conclusion, sorry. They are *very* important, because
otherwise a given algorithm that evaluates the corresponding expression
where it is allowed to do that, cannot make reasonable assumptions on
the effects and without such assumption an algorithm does not make
sense. The CopyConstructible requirements are not very much helpful
without effects requirements as well.

> The same conclusions can be
> applied to abstract lvalues or rvalues of some types.
>
> void f(int **i, int **j)
> {
> std::swap(*i, *j);
> }
>
> Even if we know that i and j are dereferenceable, we can't say whether
> *i is swappable with *j without additional knowledge about passed
> arguments.

This is true for many if not most standard algorithms, believe it or not.

>> It is already used at several places, e.g. [nullablepointer.requirements] p. 1 b. 2:
>>
>> "lvalues of type P are swappable (20.2.2),"
>
> It seems, the problem is systematic:

I see it differently as a systematic misunderstanding ;-) [I could not
resist, but I'm kidding of-course]

> A type P meets the requirements of NullablePointer if:
> [...]
> the expressions shown in Table 41 are valid and have the indicated
> semantics
>
> I believe that such method of description is far from being perfect.

Good point, the standard is not perfect. I don't deny that.

>> etc. This short-cut says exactly the same as if I would write that
>> "With lv1 and lv2 being lvalues of type T, lv1 shall be swappable with lv2" for
>> lvalues or "With rv1 and rv2 being rvalues of type T, rv1 shall be swappable
>> with rv2".
>
> Both options are incorrect if the semantics shall be under
> consideration.

It is not incorrect as explained before.

>> We don't want to repeat these lengthy sentences
>
> Happily, some descriptions have correct form:
>
> std::swap for arrays:
>
> Requires: a[i] shall be swappable with (20.2.2) b[i] for all i in
> the range [0,N).

I'm happy to see that there are at least some sentences in the standard
that are satisfactory for you ;-) [Kidding again]

>> The requirement that
>> the arguments must be valid, is already part of the basic requirements. It is
>> also part of the basic library requirements, as mentioned in 17.6.3.9 Function
>> arguments [res.on.arguments], in particular for p. 1 b 1 (for library functions)
>> and [res.on.functions] p. 2 b. 3.
>
> How this is related to my examples?

I think I explained it above.

>>> 3) change "Requres" paragraph for std::reverse as described below:
>>
>>> Requires: For each non-negative integer i< distance(first, last)/2,
>>> *next(first, i) shall be swappable with *prev(prev(last, i)).
>>
>> The short-cuts where introduced to prevent this. They have the same meaning.
>
> I strongly disagree with that. My wording clearly states that for
> every pair of iterators {x, y}, to which std::iter_swap(x, y) is
> applied, *x shall be swappable with *y. It imposes no requirements on
> any values outside the specified range (to which std::reverse
> applies).

I did not deny that. Please be careful when interpreting my response. We
must consider the requirement of an algorithm as a whole. If you start
dissecting it, this can be a different example.

> In particular, the call std::reverse(a + 0, a + 2) in the
> example above is well-defined in spite of the fact that a[0] is not
> swappable with a[2].

Sure, I don't disagree with that. And I don't think that the wording
which is used by the standard contradicts with that.

> In contrast, use of generalized forms "swappable
> expression" and "ValueSwappable type" does not allow to consider
> concrete subset of expressions.

I disagree with that interpretation and I think I have given enough
evidence for that.

> This is clearly incorrect style of description. It would be really
> funny to see such specification in an International Standard.

You are free to laugh about it ;-)

HTH & Greetings from Bremen,

Daniel Krügler


Nikolay Ivchenkov

unread,
Dec 18, 2010, 5:48:59 PM12/18/10
to
On 16 Dec, 14:20, Daniel Kru"gler <daniel.krueg...@googlemail.com>
wrote:
>

The wording

An rvalue or lvalue t is swappable if and only if t is swappable

with any rvalue or lvalue, respectively, of type T.

says nothing about a context forming a subset of considered lvalues/
rvalues.

> Let me emphasize that this is nothing special about the swappable
> requirements. It applies to all other requirements set if they are
> imposed on algorithms.

That doesn't automatically imply that such method of description is
correct if the description is intended to be formal. Swappable
requirements can be used for illustration of general problems.

> > In general, for a given expression t, there are 2 subsets of
> > expressions with the same type and value category (lvalue/rvalue):
> > 1) subset of expressions which are swappable with t and
> > 2) subset of expressions which are not swappable with t.
>
> > Both subsets may be non-empty. For example, in the sample above a[0]
> > is swappable with a[1], but a[0] is not swappable with a[2].
>
> This interpretation exceeds what the standard says.

Says or just intended to say?

> There is no
> *specific* requirement that a[0] is swappable. What you are presenting
> here is your interpretation of what the standard says.

My interpretations are literal interpretations of the given wordings.
If literal interpretation of the standard is not usable, it's not my
fault.

> > so required effects shall not be a part of a type classification.
>
> This is a false conclusion, sorry.

I don't see any evidence against such conclusion.

> They are *very* important, because
> otherwise a given algorithm that evaluates the corresponding expression
> where it is allowed to do that, cannot make reasonable assumptions on
> the effects and without such assumption an algorithm does not make
> sense. The CopyConstructible requirements are not very much helpful
> without effects requirements as well.

I did not say that required effects shall be completely excluded from
consideration. Description of required effects is important, but I
don't see objective reasons to define such requirements as a part of
restrictions on a template argument where actual compliance may depend
on dynamically evaluated values (as in examples above).

> > In particular, the call std::reverse(a + 0, a + 2) in the
> > example above is well-defined in spite of the fact that a[0] is not
> > swappable with a[2].

> Sure, I don't disagree with that. And I don't think that the wording
> which is used by the standard contradicts with that.

We can't determine whether the current rules contradict with that if
the rules just look similar to formal, but actually they are not
formal, and they can be interpreted in different ways. I believe that
some ordinary informal description might be more clear than such
formal-like description.

> > In contrast, use of generalized forms "swappable
> > expression" and "ValueSwappable type" does not allow to consider
> > concrete subset of expressions.
>
> I disagree with that interpretation and I think I have given enough
> evidence for that.

Every correct formal description makes it possible to literally and
unambiguously interpret all statements of the description and
logically deduce (without applying such things as incomplete induction
and telepathic powers) every aspect which is intended to be defined by
the given description. The current model of defining library
requirements does not satisfy this criterion.

Considering particular case of std::reverse, I don't see any rationale
for presence of the currently defined "Requires" paragraph. Reasonable
preconditions for std::reverse can be logically derived from the
"Effects" paragraph, while the intended formal preconditions cannot be
logically derived from the given "Requires" paragraph (we have to use
non-obvious methods of intuitive (not purely logical) proof to ensure
that a given use of std::reverse is well-defined). IMO, this situation
is ridiculous.

> > This is clearly incorrect style of description. It would be really
> > funny to see such specification in an International Standard.
>
> You are free to laugh about it ;-)

I can even propose some improvement:

In 5.6/3:
[remove]The binary * operator indicates multiplication[/remove]
[insert]the value of expression 2 * 3 shall be 6 [Note: if you doubt
about the value of 3 * 4, ask Daniel Krugler][/insert]

Daniel Krügler

unread,
Dec 19, 2010, 3:35:37 PM12/19/10
to

Am 18.12.2010 23:48, schrieb Nikolay Ivchenkov:
> On 16 Dec, 14:20, Daniel Kru"gler<daniel.krueg...@googlemail.com>
> wrote:
>> These are both questions different from the original one. The original
>> example is one, where std::swap(arr[0], arr[1]) *is* evaluated.
>
> The wording
>
> An rvalue or lvalue t is swappable if and only if t is swappable
> with any rvalue or lvalue, respectively, of type T.
>
> says nothing about a context forming a subset of considered lvalues/
> rvalues.

I'm unsure what context you mean here, but it definitively is required
to apply the context to call a swap function as described in the
[swappable.requirements] p. 3. If you are referring to the question
whether the Swappable requirements are imposing the requirement that
there shall be no pre-conditions for a swap call, I agree that the
wording *could* me misinterpreted, but I'm not sure that the current
state is really a defect. I could also misinterpret that the
EqualityComparable or LessThanComparable requirements impose that these
are total function without any possible domain restrictions. But this is
not the case as it is not for the swappable functions. So any type that
satisfies the EqualityComparable, LessThanComparable or any of the
Swappable requirements is free to impose pre-conditions on the
corresponding operations.

>> Let me emphasize that this is nothing special about the swappable
>> requirements. It applies to all other requirements set if they are
>> imposed on algorithms.
>
> That doesn't automatically imply that such method of description is
> correct if the description is intended to be formal. Swappable
> requirements can be used for illustration of general problems.

Sure, but you have to still to obey the general requirements of language
and library, especially if these are requirements that are supposed to
be useful for anyone (not only implementations).

>>> In general, for a given expression t, there are 2 subsets of

> My interpretations are literal interpretations of the given wordings.


> If literal interpretation of the standard is not usable, it's not my
> fault.

I don't think that the Standard can be read in literal interpretations,
because it does not use a strict formal language. I would appreciate to
have a standard written in a purely formal and non-ambiguous language,
but I think we are far away from that.

> I did not say that required effects shall be completely excluded from
> consideration. Description of required effects is important, but I
> don't see objective reasons to define such requirements as a part of
> restrictions on a template argument where actual compliance may depend
> on dynamically evaluated values (as in examples above).

Sorry, I didn't want to lay words in your mouth that you didn't intend
to say or to mean. If you don't like this decision, I consider this as a
personal preference, but note that even in concept time the library had
introduced purely "syntactical" as well as "semantic" concepts. This was
a very intentional decision, because both kind of concepts have a long
tradition in C++. The very advantage of language concepts is that they
help to discriminate when we want to impose semantic requirements and
when we don't want. Semantic requirement sets are usually invented to
reduce wording duplication in many other places of the Standard. This is
a purely economical decision.

> We can't determine whether the current rules contradict with that if
> the rules just look similar to formal, but actually they are not
> formal, and they can be interpreted in different ways. I believe that
> some ordinary informal description might be more clear than such
> formal-like description.

I don't disagree with you, but the problem is where to make the cut here.

> Every correct formal description makes it possible to literally and
> unambiguously interpret all statements of the description and
> logically deduce (without applying such things as incomplete induction
> and telepathic powers) every aspect which is intended to be defined by
> the given description. The current model of defining library
> requirements does not satisfy this criterion.

Sure. We must live with an imperfect wording because there is a
restricted amount of resources to fix all these imperfections. I don't
mean that to kid you: I seriously agree with your criticism. The problem
is, that I don't expect that a general issue like "Fix all the wording
imperfections of the library" has any chance but getting an NAD Future.
Issues that have a chance, must concentrate on some particular aspects.

> Considering particular case of std::reverse, I don't see any rationale
> for presence of the currently defined "Requires" paragraph. Reasonable
> preconditions for std::reverse can be logically derived from the
> "Effects" paragraph, while the intended formal preconditions cannot be
> logically derived from the given "Requires" paragraph (we have to use
> non-obvious methods of intuitive (not purely logical) proof to ensure
> that a given use of std::reverse is well-defined). IMO, this situation
> is ridiculous.

I don't agree with the word "ridiculous" (I think this is too strong),
but I agree that there is much improvement potential, yes.

>>> This is clearly incorrect style of description. It would be really
>>> funny to see such specification in an International Standard.
>>
>> You are free to laugh about it ;-)
>
> I can even propose some improvement:
>
> In 5.6/3:
> [remove]The binary * operator indicates multiplication[/remove]
> [insert]the value of expression 2 * 3 shall be 6 [Note: if you doubt
> about the value of 3 * 4, ask Daniel Krugler][/insert]

I like your sense of humor ;-)

Happy Christmas from Bremen,

Daniel Krügler

0 new messages