首页 > 解决方案 > 为具有未知返回类型的可调用启用部分模板专业化?

问题描述

我正在开发一个解析器组合库,并且我有一个定义解析器是什么的类:

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

我部分专注于做两件事:

  1. 为方便起见,从字符串文字构建解析器
  2. 否则包装一个解析函数(这是一个可调用的)

如果我能做到这一点,那么我可以将is_convertible<T, parser<T>>我的组合器用作谓词,以禁止它们在任何不是“解析器”的东西上使用。

字符串很简单:

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

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

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

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

编辑,如果我们有某种 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_;
};

标签: c++templatesmetaprogrammingtemplate-meta-programming

解决方案


使用 SFINAE。结合 type trait ,您可以为任何在传递 a 时is_optional返回 an 的可调用类型启用特化:optionalparse_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
    >
{};

住在神螺栓上


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


推荐阅读