Is there a way to use a using-declaration inside a requires-expression

1.4k views Asked by At

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>;
3

There are 3 answers

1
Artyer On BEST ANSWER

You can put it inside a lambda:

template<class T>
concept Swappable = []{
    using std::swap;
    return requires(T& a, T& b) { swap(a, b); };
}();
8
Nicol Bolas On

Avoid using old using-based idioms. Instead, use the customization point equivalents like ranges::swap.

That is, you should not require users to use using-based idioms in their code. Provide a customization point object that does what it needs to. The operator() overloads/templates can be constrained to create the effect of the using idiom without requiring the user to actually invoke using.

ranges::swap is a good example of how this gets done.

2
Murphy M On

Interesting, the below code works well again:

#include <concepts>

    template <typename T, typename U = T>
    concept Swappable = requires(T&& a, U&& b) {
        std::swap(a, b);
    };
    
    int main() {
        static_assert(Swappable<int>); 
        static_assert(Swappable<int, int>);
        return 0;
    }

I see an example from cppconference, but I could not get expected results. Cpp reference. See code here:

template <typename T, typename U = T>
concept Swappable = requires(T&& a, U&& b) {
    swap(std::forward<T>(a), std::forward<U>(b));
    swap(std::forward<U>(b), std::forward<T>(a));
};

int main() {
    static_assert(!Swappable<int>); // Wrong, int is definately swappable!
    static_assert(!Swappable<int, int>); // Wrong, int is definately swappable!
    return 0;
}