Включение частичной специализации шаблона для вызываемого с неизвестным типом возврата? - PullRequest
0 голосов
/ 31 мая 2018

Я работаю над библиотекой комбинатора синтаксического анализатора, и у меня есть класс, который определяет, что такое синтаксический анализатор:

template <typename P, typename allow=void>
struct parser;

Что я частично специализируюсь на двух вещах:

  1. построить парсер из строкового литерала для удобства
  2. в противном случае обернуть функцию синтаксического анализа (которая вызывается)

Если я могу это сделать, тогда я могуиспользуйте is_convertible<T, parser<T>> в качестве предиката для моих комбинаторов, чтобы запретить их использование для всего, что не является «парсером».

Строка проста:

template <>
struct parser<string> {
   ...
};

Но, в общем, парсер - это простоcallable, который принимает parse_stream и возвращает необязательный результат (я нахожусь на c ++ 11, поэтому у меня фактически нет std :: option, но мы будем притворяться).Итак, что-то вроде этого:

template <typename Result>
using parser_callable = function<optional<Result>(parse_stream)>;

Так, как я могу сделать частичную специализацию шаблона для вызываемого с неизвестным типом возврата?Замечание: я не хочу преобразовывать в std :: function, просто это совместимая вызываемая «вещь».

Edit, вот пример моей идеальной возможности, если бы у нас был какой-то подтип Scala-ishограничения и сопоставление с образцом ...

template <P <: optional<R>(parse_stream)>
struct parser<P> {
     parser(P p) : p_(p) {}

     optional<R> operator(parse_stream stream) { 
         p_(stream);
     } 

private:
     P p_;
};

1 Ответ

0 голосов
/ 31 мая 2018

Используйте SFINAE.В сочетании с чертой типа is_optional вы можете включить специализацию для любого вызываемого типа, который возвращает optional при передаче parse_stream:

template <typename T>
struct is_optional
    : std::false_type
{};

template <typename T>
struct is_optional<optional<T>>
    : std::true_type
{};

template <typename P, typename = void>
struct parser;

// This assumes that you want the parser to consume the `parse_stream`.
// It checks whether the parser can be called with the type `parse_stream&&`,
// which includes functions that take it by value, by `&&`, and by `const&`.
// If you had a specific one in mind, you can specify it. For example:
// std::declval<parse_stream&>() would pass a mutable reference instead
template <typename Callable>
struct parser<Callable,
        typename std::enable_if<is_optional<
            decltype(std::declval<Callable const&>()(std::declval<parse_stream>()))
        >::value>::type
    >
{};

Live on Godbolt


std::declval<T>() - это функция, которая работает только в неоцененных контекстах, но в таком контексте выдает значение типа T&&.Таким образом,

std::declval<Callable const&>()(std::declval<parse_stream>())

является выражением, аналогичным имеющемуся:

Callable const& callable = ...;
parse_stream some_parse_stream = ...;
// the following expression:
callable(std::move(some_parse_stream));

Поскольку мы используем это в контексте, где применяется SFINAE, если callable(std::move(some_parse_stream)) было недопустимым выражением,специализация будет снята с рассмотрения.

...