Концепция функции, возвращающей шаблонный объект - PullRequest
4 голосов
/ 30 мая 2020

Я экспериментировал с добавлением концепций в constexpr json parser и изо всех сил пытаюсь определить правильную концепцию Parser. Моя первая попытка:

using parse_input_t = std::string_view;
template <typename T>
using parse_result_t = std::optional<std::pair<T, std::string_view>>;

// A parser for type `T` is a function: parse_input_t -> parse_result_t<T>
template <typename F, typename T>
concept Parser = std::is_invocable_r_v<parse_result_t<T>, F, parse_input_t>;

Проблема в том, что я хочу писать функции с подписью:

template <Parser P>
auto func(P p);

То есть я не хочу T в интерфейс.

Я могу выполнить sh то, что хочу, с помощью чего-то уродливого:

template <typename F>
concept Parser = requires(F f, parse_input_t i)
{
    requires requires(typename decltype(f(i))::value_type result)
    {      
        { f(i) } -> std::same_as<parse_result_t<decltype(result.first)>>;
    };
};

Есть ли более чистый способ сделать это? Я надеялся на что-то вроде:

template <typename F>
concept Parser = requires(F f, parse_input_t i)
{
    { f(i) } -> std::same_as<parse_result_t<auto>>;
};

Ответы [ 2 ]

2 голосов
/ 30 мая 2020

Введите вспомогательную черту, которая проверяет, является ли данный тип parse_result_t, затем используйте его в концепции decltype(f(i)):

template <typename T>
constexpr bool is_parse_result_v = false;

template <typename T>
constexpr bool is_parse_result_v<parse_result_t<T>> = true;

template <typename F>
concept Parser = requires(F f, parse_input_t i)
{
    f(i);
    requires is_parse_result_v<decltype(f(i))>;
};

DEMO


В качестве альтернативы используйте вспомогательную черту в отдельном определении концепта, чтобы избежать decltype:

template <typename T>
concept is_parse_result = is_parse_result_v<T>;

template <typename F>
concept Parser = requires(F f, parse_input_t i)
{
    { f(i) } -> is_parse_result;
};

DEMO 2

2 голосов
/ 30 мая 2020

Конечно, ты справишься.

Определите типаж для извлечения T из std::optional<std::pair<T, std::string_view>>:

template <typename T>
struct parser_type;
template <typename T>
struct parser_type<std::optional<std::pair<T, std::string_view>>> {
    using type = T;
};
template <typename T>
using parser_type_t = typename parser_type<T>::type;

Определите типаж вокруг того, который пытается вытащить T из, когда вы вызываете F:

template <typename F>
using parser_result = parser_type_t<std::invoke_result_t<F, std::string_view>>;

и построить вокруг этого концепцию:

template <typename F>
concept Parser = requires {
    typename parser_result<F>;
};

Затем вы можете использовать parser_result<F> как связанный тип парсера. Например:

struct Ints {
    auto operator()(std::string_view) -> std::optional<std::pair<int, std::string_view>>;
};

static_assert(Parser<Ints>);
static_assert(std::same_as<int, parser_result<Ints>>);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...