Swappable concepts and ADL (was Re: Return versus Side-Effect)
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! ]