是否可以在不编写自定义特征类的情况下对类型进行模式匹配?

时间:2019-06-29 05:38:30

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

由于C ++ 20概念尚未标准化,因此我将static_assert用作临时概念检查,以在不满足类型要求的情况下提供有用的错误消息。在这种情况下,我有一个函数,要求在获取结果类型之前可调用该类型:

template <typename F, typename... Args>
void example() {
  static_assert(std::is_invocable_v<F, Args...>, "Function must be callable");
  using R = std::invoke_result_t<F, Args...>;

  // ...
}

此外,我要求可调用对象的结果必须为某种std::optional,但我不知道可选内容将保留哪种类型,因此我需要从中获取该类型:

using R = // ...
using T = typename R::value_type;  // std::optional defines a value_type

但是,如果类型R没有value_type,例如如果不是预期的std::optional。我想先用static_assert进行检查,如果断言失败,还会出现另一个不错的错误消息。

我可以使用类似std::is_same_v的形式检查确切类型,但是在这种情况下,我不知道确切类型。我想检查Rstd::optional实例,而不指定它必须是哪个实例。

做到这一点的一种方法是利用助手特质:

template <typename T>
struct is_optional { static constexpr bool value = false; };

template <typename T>
struct is_optional<std::optional<T>> { static constexpr bool value = true; };

template <typename T>
constexpr bool is_optional_v = is_optional<T>::value;

...然后我可以写:

static_assert(is_optional_v<R>, "Function's result must be an optional");

这行得通,但是用一个辅助特性来污染我的命名空间只是为了一次性检查就显得有些尴尬了。我不希望在其他任何地方都需要is_optional,尽管我可以想象可能还会出现其他一次性特征,例如is_variantis_pair

所以我想知道:是否有更简洁的方法?是否可以对std::optional的实例进行模式匹配而不必定义is_optional特质及其部分专业化?

2 个答案:

答案 0 :(得分:4)

  

是否可以在std::optional实例上进行模式匹配而不必定义is_optional特征及其部分专业化?

也许对std::optional使用隐式推导法?

我的意思是...类似

using S = decltype(std::optional{std::declval<R>()});

static_assert( std::is_same_v<R, S>, "R isn't a std::optional" );

说明。

对于某些R类型,当std::optional<T>T时,std::optional{r}(对于类型为r的{​​{1}}值)应调用副本构造函数和结果值应为R类型。

否则,类型应该不同(R)。

以下是完整的编译示例。

std::optional<R>

无论如何,我支持super的建议:创建一个更通用的类型特征,将#include <iostream> #include <optional> template <typename T> bool isOptional () { using U = decltype(std::optional{std::declval<T>()}); return std::is_same_v<T, U>; } int main () { std::cout << isOptional<int>() << std::endl; // print 0 std::cout << isOptional<std::optional<int>>() << std::endl; // print 1 } 作为模板-模板参数。

答案 1 :(得分:4)

根据几位受访者的建议,我做了一个可重复使用的特征:

template <typename T, template <typename...> typename Tpl>
struct is_template_instance : std::false_type { };

template <template <typename...> typename Tpl, typename... Args>
struct is_template_instance<Tpl<Args...>, Tpl> : std::true_type { };

template <typename T, template <typename...> typename Tpl>
constexpr bool is_template_instance_v = is_template_instance<T, Tpl>::value;

...以便我可以写:

static_assert(is_template_instance_v<R, std::optional>, "Function's result must be an optional");

这与is_optional特性一样多的行和声明,但不再是一次性的;我可以使用相同的特征来检查其他种类​​的模板(例如变体和对)。因此,现在感觉像是对我的项目的有用补充,而不是克鲁格。