Re: Swappable concepts and ADL (was Re: Return versus Side-Effect)
On Apr 1, 2:18 pm, Alberto Ganesh Barbati <AlbertoBarb...@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! ]