So it contrains the input range and the predicate. It doesn't constrain the projection directly, but any errors there will be caught by the constraint on the predicate. Let's start with the constraint on the input range. In your case, you want T to be a nested range of at least unwrap_levels deep. Unfortunately, you can't use if constexpr inside a concept definition, and you also can't declare recursive concepts. But the trick is to create a type trait using recursive structs, or even better, a recursive constexpr function:
// Primary template
template<std::size_t unwrap_level, typename T>
structstatic recursive_input_range_trait:constexpr std::false_typebool is_recursive_input_range() {};
// Terminating case
template<class T>
struct recursive_input_range_trait<0,if T>:constexpr std::true_type(unwrap_level == 0) {};
// Recursing case return true;
template<std::size_t unwrap_level, } else if constexpr (std::ranges::input_rangeinput_range<T>) T>{
struct recursive_input_range_trait<unwrap_level, T>:
recursive_input_range_trait<unwrap_level return is_recursive_input_range<unwrap_level - 1,
std::ranges::range_value_t<T>>();
} else {
return false;
std::range::range_value_t<T>> {};
}
template<std::size_ttemplate<typename unwrap_levelT, typenamestd::size_t T>unwrap_level>
concept recursive_input_range =
recursive_input_range_trait<unwrap_levelis_recursive_input_range<unwrap_level, T>::value;();