Как решить, требуется предложение несовместимо
Я пытаюсь реализовать рекурсивную версиюstd::iter_value_t
с концепцией C++20, так что базовый тип
T
вложенного контейнера, такого как
std::vector<std::vector<...std::vector<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
значение. Проблема в том, что структура шаблона не может быть перегружена таким образом? Пожалуйста, помогите мне разобраться в этом.
Ожидаемый результат
recursive_iter_value_t<std::vector<std::vector<int>>>
является
int
.
1 ответ
Куча всего.
Во-первых, в C++20 уже есть концепция итерируемости: она называетсяstd::ranges::range
.
Во-вторых, начиная с C++11, нет причин использовать
typedef
. Всегда предпочитаю. Использование
typename
также был недействителен. Так
using type = T;
скорее, чем
typedef typename T type;
Причина этого в том, что
using
намного мощнее (у вас могут быть шаблоны псевдонимов в дополнение к обычным псевдонимам) и что это гораздо более разумный синтаксис (имя, которое вы вводите, находится слева от
=
а не... практически где угодно).
В-третьих, способ правильно написать частичную специализацию шаблона ограниченного класса состоит в том, что первичный элемент не ограничен:
template <typename T>
struct recursive_iter_value_t_detail {
using type = T;
};
а остальные (а) более ограничены, чем первичные, и (б) должны фактически быть специализациями (см.
<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> {
// ...
};
Наконец, ваш рекурсивный шаг назад. Если это диапазон, вам нужно сначала распаковать диапазон , а затем рекурсивно. В настоящее время вы сначала рекурсируете, но с тем же типом, так что это бесконечная рекурсия. Так должно быть:
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>>
{ };
В общем, [демо] :
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;