I want to test whether a type can be passed to some function, but I'd like to use ADL on the function lookup and include a function from a certain namespace.
Consider this code:
#include <utility>
#include <vector>
template<class T>
concept Swappable = requires(T& a, T& b)
{
swap(a,b);
};
static_assert(Swappable<std::vector<int>>); // #1
static_assert(Swappable<int>); // #2
#1 succeeds, it finds std::swap
because std
is an associated namespace of std::vector<int>
. But #2 fails, a built-in type has no associated namespace.
How would I write something like:
template<class T>
concept Swappable = requires(T& a, T& b)
{
using std::swap; // illegal
swap(a,b);
};
AFAIK, you're not allowed to use a using-declaration inside a requires-expression.
(NOTE Although there is a perfectly fine standard C++ concept for this, std::swappable
, this example uses swap
for exposition only. I'm not particularly looking to test whether something is actually swappable, I'm just trying to find a way to implement such a concept where a customization function has a default implementation in a known namespace, but might have overloads in an associated namespace.)
EDIT As a workaround, I can implement the concept in a separate namespace where the names are pulled in. Not too happy about it but it works.
namespace detail
{
using std::swap;
template<class T>
concept Swappable = requires(T& a, T& b)
{
swap(a,b);
};
}
// and then either use it
using detail::Swappable;
// or redefine it
template<class T>
concept Swappable = detail::Swappable<T>;
Swappable
, you still have tousing std::swap
to be safe – Bipetalousstd::vector
Argument Dependent Lookup (ADL) kicks in. You should just usestd::vetor(a, b)
; – Uprightusing
declarations everywhere either. The language is not meant for you to be able to do this, and your interface should accept that. – Autreystd::vetor(a, b);
? – Uprightbegin(c)
/end(c)
, similar forget<N>(t)
for tuples. Unfortunately, this not only applies tostd
. – Athaliastd::vetor(a,b)
" (I'm assuming you meanvector
here instead ofvetor
, but that doesn't make it clearer). – Athaliastd::swappable
– Peevishstd::swap(a, b);
:). – Uprightstd
, so if you want to implementswap()
for your own type, you'll need to do that outside ofstd
. As a result, any generic code directly callingstd::swap()
bypasses your specialized implementation. That's why it's recommended to do theusing std::swap; swap(a, b);
idiom in generic code, because that way it finds thestd
implementation as well as any other associated namespace ofa
andb
. Of course,std::ranges::swap()
takes care of this boilerplate for you, as @NicolBolas explained, so that would be the preferred way. – Athaliastd::swap
? – Uprightstd::swap
butswap
. That's why you want to callswap
instead ofstd::swap
. – Athalia