如何解决 requires 子句不兼容

How to solve requires clause is incompatible

我正在尝试使用 C++20 概念实现递归版本 std::iter_value_t,以便可以检索 std::vector<std::vector<...std::vector<T>...>> 等嵌套容器的基本类型 T。实验实现如下

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;

尝试编译此代码后,弹出了唯一的错误消息 'recursive_iter_value_t_detail': requires clause is incompatible with the declaration,我不确定 requires clause is incompatible with the declaration 是什么意思。问题是template struct不能这样重载吗?请帮我解决这个问题。

recursive_iter_value_t<std::vector<std::vector<int>>> 的预期输出是 int

很多东西。

首先,C++20 已经有了可迭代性的概念:它叫做 std::ranges::range

其次,自C++11以来,没有理由使用typedef。总是喜欢using。使用 typename 也是无效的。所以 using type = T; 而不是 typedef typename T type; 这样做的原因是 using 更强大(除了普通别名之外你还可以有别名模板)并且它是一个更明智的语法(名称您要介绍的是 = 的左侧,而不是...基本上任何地方)。

第三,正确编写约束class模板偏特化的方法是主是无约束的:

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

和其他 (a) 主要限制更多,并且 (b) 实际上必须是专业化的(请参阅语法中的额外 <T>):

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

也可以这样拼写:

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

最后,你的递归步骤是倒退的。如果它是一个范围,则需要 first 解包并且范围 then 递归。您目前首先递归——但在同一类型上,所以这是无限递归。所以应该是:

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

你可以把它写得更短:

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

总而言之[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;