Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I combine several return type requirements of C++20 constraints into one return type requirement?

Currently, I have implemented the Allocator concept (which refers to the Boost proposal) using C++20 constraints and concepts:

#include <concepts>
#include <iterator>

template <class A>
concept allocator =
  std::copy_constructible<A> &&
  std::equality_comparable<A> &&
  requires(A a) {
    { a.allocate(0) } -> std::regular;
    { a.allocate(0) } -> std::constructible_from<std::nullptr_t>;
    { a.allocate(0) } -> std::equality_comparable_with<std::nullptr_t>;
    { a.allocate(0) } -> std::random_access_iterator;
    { *a.allocate(0) } -> std::same_as<typename A::value_type&>;
  };

You can see there are several return-type-requirements for the same allocate function. Is there some way to combine them into one single return-type-requirement like the following?

{ a.allocate(0) } -> std::regular &&
                     std::constructible_from<std::nullptr_t> &&
                     std::equality_comparable_with<std::nullptr_t> &&
                     std::random_access_iterator;
like image 287
康桓瑋 Avatar asked Jul 29 '20 06:07

康桓瑋


2 Answers

No, you cannot combine type constraints like this, but you can create a named concept

template <class A>
concept allocate_result =
    std::regular<A> &&
    std::constructible_from<A, std::nullptr_t> &&
    std::equality_comparable_with<A, std::nullptr_t> &&
    std::random_access_iterator<A>;

and use it

{ a.allocate(0) } -> allocate_result;
{ *a.allocate(0) } -> std::same_as<typename A::value_type&>;

You can also parameterize allocator_result by the container type and incorporate the last condition:

template <class A, class Cont>
concept allocator_result =
    std::regular<A> &&
    std::constructible_from<A, std::nullptr_t> &&
    std::equality_comparable_with<A, std::nullptr_t> &&
    std::random_access_iterator<A> &&
    std::same_as<typename std::remove_pointer<A>::type, typename Cont::value_type&>;

template <class A>
concept allocator =
  std::copy_constructible<A> &&
  std::equality_comparable<A> &&
  requires(A a) {
    { *a.allocate(0) } -> allocator_result<A>;
  };
like image 104
n. 1.8e9-where's-my-share m. Avatar answered Nov 04 '22 08:11

n. 1.8e9-where's-my-share m.


For this particular case, you should follow the concept more closely. The return type of allocator_traits<A>::allocate is required to be allocator_traits<A>::pointer. So that's what you should test. It is allocator_traits<A>::pointer that must satisfy the various constraints of contiguous iterator and nullable pointer.

So the code should look like this:

template<typename P>
concept nullable_pointer =
  std::regular<P> &&
  std::convertible_to<std::nullptr_t, P> &&
  std::assignable_from<P&, std::nullptr_t> &&
  std::equality_comparable_with<P, std::nullptr_t>
  

template<typename P>
concept allocator_pointer =
  nullable_pointer<P> &&
  std::contiguous_iterator<P>;

template<typename A>
concept allocator =
  std::copy_constructible<A> &&
  std::equality_comparable<A> &&
  requires(A a)
  {
    { a.allocate(0) } -> allocator_pointer;
  };
like image 8
Nicol Bolas Avatar answered Nov 04 '22 08:11

Nicol Bolas