【问题标题】:Can I pattern-match a type without writing a custom trait class?我可以在不编写自定义特征类的情况下对类型进行模式匹配吗?
【发布时间】:2019-06-29 05:38:30
【问题描述】:

由于 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...>;

  // ...
}

另外,我要求 callable 的结果必须是某种std::optional,但我不知道 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 特征及其部分专业?

【问题讨论】:

  • “简洁”是指更少的行,还是意味着更少的命名空间范围声明?
  • 您可以创建一个可重用的 trait,允许您定义 constexpr bool is_optional_v = is_template_of&lt;std::optional, T&gt;::value; 并且可以重用于一对/变体与一个衬垫。

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


【解决方案1】:

根据几位受访者的建议,我提出了一个可重复使用的 trait:

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 trait 一样多的行和声明,但它不再是一次性的;我可以使用相同的特征来检查其他类型的模板(如变体和对)。所以现在它感觉像是对我的项目的有用补充,而不是 kluge。

【讨论】:

  • 一个可重复使用的 triait 的好主意 :-) - 无论如何,我建议进行一些改进:您可以从 std::false_type(主模板)或 std::true_type 继承而不是定义 static constexpr bool value (专业化);这样您就可以继承std::integral_constant 中存在的设施(例如:operator()operator value_type)。
【解决方案2】:

我可以对 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 对于某些 T 类型为 std::optional&lt;T&gt; 时,std::optional{r}(对于 r 类型为 R 的值)应调用复制构造函数,并且结果值应为相同类型 @ 987654331@.

否则,类型应该不同(std::optional&lt;R&gt;)。

以下是完整的编译示例。

#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   
 }

无论如何,我支持 super 的建议:创建一个更通用的类型特征,接收 std::option 作为模板模板参数。

【讨论】:

  • 聪明。我想我会采用可重用的特征方法,但我喜欢这个想法。看到这个,我可能会在某个地方找到这个技巧的另一种用途。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-18
  • 1970-01-01
  • 2012-08-06
  • 1970-01-01
  • 2013-06-28
  • 1970-01-01
相关资源
最近更新 更多