Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to solve requires clause is incompatible

I am trying to implement a recursive version std::iter_value_t with C++20 concept so that the base type T of nested container like std::vector<std::vector<...std::vector<T>...>> can be retrieved. The experimental implementation is as below.

template<typename T>
concept is_iterable = requires(T x)
{
    *std::begin(x);
    std::end(x);
};

template<typename T> requires (!is_iterable<T>)
struct recursive_iter_value_t_detail
{
    typedef typename T type;
};

template<typename T> requires (is_iterable<T>)
struct recursive_iter_value_t_detail
{
    typedef typename std::iter_value_t<typename recursive_iter_value_t_detail<T>::type> type;
};

template<typename T>
using recursive_iter_value_t = typename recursive_iter_value_t_detail<T>::type;

After trying to compile this code, the only error message 'recursive_iter_value_t_detail': requires clause is incompatible with the declaration popped up and I am not sure what's requires clause is incompatible with the declaration meaning. Is the problem that template struct can't be overloaded like this? Please help me to figure this out.

The expected output of recursive_iter_value_t<std::vector<std::vector<int>>> is int.

like image 998
JimmyHu Avatar asked Oct 25 '25 05:10

JimmyHu


1 Answers

Lots of things.

First, C++20 already has a concept for iterability: it's called std::ranges::range.

Second, since C++11, there is no reason to use typedef. Always prefer using. The use of typename was also invalid. So using type = T; rather than typedef typename T type; The reason for this is that using is much more powerful (you can have alias templates in addition to normal aliases) and that it's a much saner syntax (the name you're introducing is on the left of the = rather than... basically anywhere at all).

Third, the way to correctly write a constrained class template partial specialization is that the primary is unconstrained:

template <typename T>
struct recursive_iter_value_t_detail {
    using type = T;
};

and the others are (a) more constrained than the primary and (b) have to actually be specializations (see the extra <T> in the syntax):

template <typename T> requires std::ranges::range<T>
struct recursive_iter_value_t_detail<T> {
    // ...
};

This can also be spelled:

template <std::ranges::range T>
struct recursive_iter_value_t_detail<T> {
    // ...
};

Lastly, your recursive step is backwards. If it's a range, you need to first unpack and the range then recurse. You're currently first recursing -- but on the same type, so that's infinite recursion. So it should be:

using type = recursive_iter_value_t_detail<std::iter_value_t<T>>::type;

which you can write shorter as:

template <std::ranges::range T>
struct recursive_iter_value_t_detail<T>
    : recursive_iter_value_t_detail<std::iter_value_t<T>>
{ };

Putting it altogether [demo]:

template<typename T>
struct recursive_iter_value_t_detail
{
    using type = T;
};

template <std::ranges::range T>
struct recursive_iter_value_t_detail<T>
    : recursive_iter_value_t_detail<std::iter_value_t<T>>
{ };

template<typename T>
using recursive_iter_value_t = typename recursive_iter_value_t_detail<T>::type;
like image 80
Barry Avatar answered Oct 26 '25 18:10

Barry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!