我正在使用解析器组合器库,并且我有一个定义解析器的类:
template <typename P, typename allow=void>
struct parser;
我部分专门做两件事:
如果我能做到这一点,那么我可以使用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_;
};
答案 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
>
{};
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))
是无效表达式,则将不考虑专门化。