为具有未知返回类型的可调用启用部分模板专门化?

时间:2018-05-31 18:20:08

标签: c++ templates metaprogramming template-meta-programming

我正在使用解析器组合器库,并且我有一个定义解析器的类:

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

我部分专门做两件事:

  1. 为方便起见,从字符串文字构建解析器
  2. 否则包装解析函数(可调用)
  3. 如果我能做到这一点,那么我可以使用is_convertible<T, parser<T>>作为我的组合子的谓词,禁止他们使用任何不是&#34;解析器&#34;。

    字符串很简单:

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

    但是,一般来说,解析器只是一个可调用的,它接受一个parse_stream并返回一个可选的结果(我在c ++ 11中使用,所以我实际上没有std :: optional,但是我们&# 39;假装)所以,像这样:

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

    那么如何为具有未知返回类型的可调用对象进行部分模板特化?注意我不想转换为std :: function,只是它是一个兼容的可调用的东西&#34;。

    编辑,这是我理想能力的一个例子,如果我们有某种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 :(得分:2)

使用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))是无效表达式,则将不考虑专门化。