From: Alberto Ganesh Barbati on
Niels Dekker - no return address ha scritto:
> Alberto Ganesh Barbati wrote:
>
>> On this topic, the very recent paper n2590
>> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2590.pdf)
>> proposes to introduce a new concept MemberSwappable, that would help
>> avoid the need for the three overloads.
>
> Thank you for the pointer as well! Still I think it would make sense to
> add generic std::swap overloads as well, to support swapping an rvalue.
> Those overloads could be implemented by simply doing an unqualified swap
> function call:
>
> namespace std
> {
> // Supporting an rvalue at the left hand side.
> template<class T> void swap(T&& a, T& b)
> {
> swap(a, b);
> }
>
> // Supporting an rvalue at the right hand side.
> template<class T> void swap(T& a, T&& b)
> {
> swap(a, b);
> }
> }
>
> Note that 'a' and 'b' would no longer be rvalues within the body of
> those functions, so there would't be an infinite recursion! Instead,
> argument dependent lookup (ADL) could be applied to find the most
> appropriate swap.
>
> I think such std::swap overloads would be useful, for the sake of
> generic programming. What do you think?
>

I think that your idea gives me the basis for a broader approach to the
swappable mess^H^H^H^Hissue. ADL is both a blessing and a curse to whole
concept of swapping. Consider a generic algorithm that needs to swap two
variables:

template <class T>
void my_clever_algorithm(T x, T y)
{
// ... do something clever
// (*) swap x and y
// ... do something else clever
}

how do you write step (*)?

If you write swap(x, y) you may reach a user-provided swap() through ADL
but if you don't find it the code won't compile even if T is
MoveConstructible and MoveAssignable (for example if T is a basic type!).

If you write std::swap(x, y) instead, the code will probably work with
all suitable types, but it will miss the possibility to use an optimized
swap() that would be reachable through ADL.

Someone suggested this idiom:

template <class T>
void my_clever_algorithm(T x, T y)
{
// ... do something clever
using std::swap;
swap(x, y);
// ... do something else
}

which does the right thing in all cases, but, frankly... it sucks!

What if the user could write std::swap() and do the right thing in every
cases? I admit that I haven't digested concepts fully, so the feedback
of some concept Guru is mostly welcome, but I think I found a way to get
such feature. Consider this code (for the sake of brevity, I follow the
approach of allowing rvalue-rvalue swaps, the code could easily be
modified to disallow that case):

namespace std
{
template <ClassType T>
auto concept AdlSwappable {
void swap(T&, T&); // notice, no need for && here
};

template <ClassType T>
auto concept MemberSwappable {
void T::swap(T&); // no need for && here too
};

template <class T>
requires !MemberSwappable<T>
&& !AdlSwappable<T>
&& MoveConstructible<T>
&& MoveAssignable<T>
void swap(T&& a, T&& b)
{
T x(std::move(a));
a = std::move(b);
b = std::move(x);
}

template <class T>
requires !MemberSwappable<T> && AdlSwappable<T>
void swap(T&& a, T&& b)
{
swap(a, b);
}

template <class T>
requires MemberSwappable<T>
void swap(T&& a, T&& b)
{
a.swap(b);
}

// and finally:
template <ClassType T>
requires MemberSwappable<T> || AdlSwappable<T>
|| (MoveConstructible<T> && MoveAssignable<T>)
auto concept Swappable {
};
}

Now a user-defined type T could provide "swappability" in three
different ways, in order of preference:

1) by T having a member function swap()
2) by having a free function swap in an namespace associated with T
3) by T being MoveConstructible and MoveAssignable

In any case and even if T only provides 1) or 2) with the "legacy"
signature bearing T& and not T&&, the following code:

template <class T>
void my_clever_algorithm(T x, T y)
{
// ... do something clever
std::swap(x, T()); // notice, rvalue used here!
// ... do something else
}

will compile and will always do the right thing! In fact the definition
of concept Swappable would be different from the one currently proposed
in paper N2502: now Swappable means that you can truly apply
std::swap(), regardless of how such algorithm is implemented, the N2502
concept of Swappable being replaced with AdlSwappable which is just one
possible ingredient of the Swappable soup.

Apart from the possible mistakes I may have inserted in the concept
code, does it make sense?

Ganesh

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

From: Yechezkel Mett on
On Apr 1, 2:18 pm, Alberto Ganesh Barbati <AlbertoBarb...(a)libero.it>
wrote:
> I think that your idea gives me the basis for a broader approach to the
> swappable mess^H^H^H^Hissue. ADL is both a blessing and a curse to whole
> concept of swapping. Consider a generic algorithm that needs to swap two
> variables:
>
> template <class T>
> void my_clever_algorithm(T x, T y)
> {
> // ... do something clever
> // (*) swap x and y
> // ... do something else clever
> }
>
> how do you write step (*)?
>
> If you write swap(x, y) you may reach a user-provided swap() through ADL
> but if you don't find it the code won't compile even if T is
> MoveConstructible and MoveAssignable (for example if T is a basic type!).
>
> If you write std::swap(x, y) instead, the code will probably work with
> all suitable types, but it will miss the possibility to use an optimized
> swap() that would be reachable through ADL.
....
>
> What if the user could write std::swap() and do the right thing in every
> cases? I admit that I haven't digested concepts fully, so the feedback
> of some concept Guru is mostly welcome, but I think I found a way to get
> such feature. Consider this code (for the sake of brevity, I follow the
> approach of allowing rvalue-rvalue swaps, the code could easily be
> modified to disallow that case):
>
> namespace std
> {
> template <ClassType T>
> auto concept AdlSwappable {
> void swap(T&, T&); // notice, no need for && here
> };
>
> template <ClassType T>
> auto concept MemberSwappable {
> void T::swap(T&); // no need for && here too
> };
>
> template <class T>
> requires !MemberSwappable<T>
> && !AdlSwappable<T>
> && MoveConstructible<T>
> && MoveAssignable<T>
> void swap(T&& a, T&& b)
> {
> T x(std::move(a));
> a = std::move(b);
> b = std::move(x);
> }
>
> template <class T>
> requires !MemberSwappable<T> && AdlSwappable<T>
> void swap(T&& a, T&& b)
> {
> swap(a, b);
> }
>
> template <class T>
> requires MemberSwappable<T>
> void swap(T&& a, T&& b)
> {
> a.swap(b);
> }
>
> // and finally:
> template <ClassType T>
> requires MemberSwappable<T> || AdlSwappable<T>
> || (MoveConstructible<T> && MoveAssignable<T>)
> auto concept Swappable {
> };
>
> }
>
> Now a user-defined type T could provide "swappability" in three
> different ways, in order of preference:
>
> 1) by T having a member function swap()
> 2) by having a free function swap in an namespace associated with T
> 3) by T being MoveConstructible and MoveAssignable

Perhaps the following would be better (assuming it works!)

namespace std
{
auto concept Swappable<typename T> {
void swap(T&, T&)
{
T x(std::move(a));
a = std::move(b);
b = std::move(x);
}
};

auto concept MemberSwappable<typename T> : Swappable<T> {
void T::swap(T&);
void swap(T& lhs, T& rhs) { lhs.swap(rhs); }
};

template <Swappable T>
void swap(T&& a, T&& b)
{
Swappable<T>::swap(a, b);
}
}

Yechezkel Mett


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

From: Alberto Ganesh Barbati on
Yechezkel Mett ha scritto:
>
> Perhaps the following would be better (assuming it works!)
>
> namespace std
> {
> auto concept Swappable<typename T> {
> void swap(T&, T&)
> {
> T x(std::move(a));
> a = std::move(b);
> b = std::move(x);
> }
> };
>
> auto concept MemberSwappable<typename T> : Swappable<T> {
> void T::swap(T&);
> void swap(T& lhs, T& rhs) { lhs.swap(rhs); }
> };
>
> template <Swappable T>
> void swap(T&& a, T&& b)
> {
> Swappable<T>::swap(a, b);
> }
> }
>

I see... very nice. I'm starting to like concepts ;) and possibly I'm
also starting to learn how to use them effectively, thanks!

Please correct me if I'm wrong, but in this case swappability would be
achieved by:

1) by providing a explicit concept map for Swappable<T>
2) by T having a member function swap()
3) by having a free function swap in a namespace associated with T
4) by T being MoveConstructible and MoveAssignable

so we would provide the user a third "customization point" over the
basic algorithm: that would be great! (and we don't even need the
AdlSwappable concept around!)

I hope Alisdair Meredith (the author of paper N2591) is reading this
thread, as I'd love to hear feedback from him.

Cheers,

Ganesh

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

From: Yechezkel Mett on
On Apr 2, 2:09 pm, Alberto Ganesh Barbati <AlbertoBarb...(a)libero.it>
wrote:
> Yechezkel Mett ha scritto:
> > Perhaps the following would be better (assuming it works!)
>
> > namespace std
> > {
> > auto concept Swappable<typename T> {
> > void swap(T&, T&)
> > {
> > T x(std::move(a));
> > a = std::move(b);
> > b = std::move(x);
> > }
> > };
>
> > auto concept MemberSwappable<typename T> : Swappable<T> {
> > void T::swap(T&);
> > void swap(T& lhs, T& rhs) { lhs.swap(rhs); }
> > };
>
> > template <Swappable T>
> > void swap(T&& a, T&& b)
> > {
> > Swappable<T>::swap(a, b);
> > }
> > }
>
> I see... very nice. I'm starting to like concepts ;) and possibly I'm
> also starting to learn how to use them effectively, thanks!
>
> Please correct me if I'm wrong, but in this case swappability would be
> achieved by:
>
> 1) by providing a explicit concept map for Swappable<T>
> 2) by T having a member function swap()
> 3) by having a free function swap in a namespace associated with T
> 4) by T being MoveConstructible and MoveAssignable

That was the idea, but after working my way through bits of N2501 I
don't think it'll work. Worse yet, sometimes it will and sometimes it
won't depending on instantiation order. If I've understood it
correctly :-) and depending on the precise meaning of the word "need".

I think the following should do the trick:

namespace std
{
auto concept Swappable<typename T> {
void swap(T& lhs, T& rhs)
{
T x(std::move(lhs));
lhs = std::move(rhs);
rhs = std::move(x);
}
};

auto concept MemberSwappable<typename T> {
void T::swap(T&);
};

template<MemberSwappable T>
concept_map Swappable<T> {
swap(T& lhs, T& rhs)
{
lhs.swap(rhs);
}
};

template <Swappable T>
void swap(T&& a, T&& b)
{
Swappable<T>::swap(a, b);
}
}

Yechezkel Mett


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

From: Alberto Ganesh Barbati on
Yechezkel Mett ha scritto:
>
> I think the following should do the trick:
>
> namespace std
> {
> auto concept Swappable<typename T> {
> void swap(T& lhs, T& rhs)
> {
> T x(std::move(lhs));
> lhs = std::move(rhs);
> rhs = std::move(x);
> }
> };
>
> auto concept MemberSwappable<typename T> {
> void T::swap(T&);
> };
>
> template<MemberSwappable T>
> concept_map Swappable<T> {
> swap(T& lhs, T& rhs)
> {
> lhs.swap(rhs);
> }
> };
>
> template <Swappable T>
> void swap(T&& a, T&& b)
> {
> Swappable<T>::swap(a, b);
> }
> }
>

Sounds good to me (which isn't very much as I'm not a concepts expert
;). The only thing that may be missing is the improved diagnostic that
concepts should bring in case you try to apply std::swap() to a type
that is neither explicitly Swappable, nor MemberSwappable nor
MoveConstructible/MoveAssignable. I guess the compiler would just
stumble parsing the default implementation of Swappable<T>::swap() with
a possibly difficult-to-read error message about a missing constructor
or assignment operator.

Anyway, I believe we agree about the following approach, which is
substantially different from the current proposals N2572/N2573 as well
as the revised proposal N2590:

1) the default implementation requiring MoveConstructible and
MoveAssignable is put in some concept or concept_map, rather than in
some template (or specialization or overload) of std::swap() itself

2) std::swap() just acts as a selector among the different possible
implementations of swapping

That would bring us:

1) std::swap() (to *always* be qualified with std::!) would become the
true universal swapping algorithm, valid for every type T, provided that T:

a) has a explicit concept_map for Swappable, or
b) is MemberSwappable, or
c) there is an ADL-reachable swap() free function, or
d) is MoveConstructible and MoveAssignable

This will once and for all solve the "to ADL or not to ADL" dilemma
about swapping and clarify the "extension points" of the algorithm.

2) as every library class or class template which is supposed to be
Swappable is or can be made MemberSwappable, we could safely remove all
swap() free member functions and function templates from the whole
library (achieving the same goal as N2590).

Ganesh

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