7

Is it possible to pass a concept as a template parameter? For example:

I want to do something like this:

template <typename t, typename u> concept range_of =
    range<t> &&
    requires (t a) {
        {*a.begin()} -> std::same_as<u &>;
    };

But instead of giving the exact type u I want to give it a concept:

template <typename t, {{concept u}}> concept constrained_range =
    range<t> &&
    requires (t a) {
        {*a.begin()} -> u;
    };
egst
  • 1,605
  • 3
  • 18
  • 25
  • 1
    I think not, also it feels that it could lead to re-emergence of Russell's paradox :P – Swift - Friday Pie Jul 26 '20 at 09:36
  • 1
    You cannot pass a concept, but you can pass a template whose template parameter are constrained or a struct with a member function template that constrain the argument type. – Oliv Jul 26 '20 at 12:18

2 Answers2

11

It's possible to cheat a bit since we can pass lambda functions as template arguments ; a concept is just a meta-function from the domain of types to the boolean domain after all.

Here is an example:

template<auto F, typename T>
using check = std::conditional_t<
      F.template operator()<T>()
    , std::true_type
    , std::false_type>;

#define CONCEPT(TheConcept) \
  [] <typename T> () consteval { return TheConcept<T>; }

static_assert(check<CONCEPT(std::floating_point), float>::value);

Demo on Godbolt: https://gcc.godbolt.org/z/fGf45nqTc

Jean-Michaël Celerier
  • 7,412
  • 3
  • 54
  • 75
  • 1
    That's a nice trick. I'll accept this as the answer, because it doesn't require using a struct instead of a concept. – egst Oct 26 '21 at 07:33
1

Currently it's not possible to pass a concept as a template parameter, but you can get around it by passing a template template:

#include <optional>

template <typename T>
concept Optional = requires {
  typename T::value_type;
  // ...
};


template <template<typename> typename Q, typename T>
concept OptionalOf = Optional<T> && Q<typename T::value_type>::value;

which you can use like this (on compiler-explorer):

constexpr bool is_optional_of_int = OptionalOf<std::is_integral, std::optional<int>>;

or this:

template <typename T>
struct constrained_range {
  static constexpr bool value = range<T> && requires (T a) {
        {*a.begin()} -> u;
    };
};

constexpr bool is_optional_of_constrained_range = OptionalOf<constrained_range, std::optional<int>>;
The Moisrex
  • 1,857
  • 1
  • 14
  • 16