Могу ли я сопоставить шаблон с типом, не написав собственный класс черт? - PullRequest
7 голосов
/ 29 июня 2019

Поскольку концепции 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, но в этом случаеЯ не знаю точного типа.Я хочу проверить, что R является некоторым экземпляром std::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_variant или is_pair.

Так что мне интересно: есть ли более краткий способ сделать это? Могу ли я выполнить сопоставление с образцом на экземплярах std::optional без необходимости определения черты is_optional и ее частичной специализации?

Ответы [ 2 ]

4 голосов
/ 29 июня 2019

Следуя предложению нескольких респондентов, я применил черту многократного использования:

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, но она больше не является одноразовой;Я могу использовать ту же черту для проверки других видов шаблонов (например, вариантов и пар).Теперь я чувствую себя полезным дополнением к моему проекту, а не клугем.

4 голосов
/ 29 июня 2019

Могу ли я выполнить сопоставление с образцом на экземплярах 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 типа R) должен вызывать конструктор копирования, и результирующее значение должно быть того же типа R.

В противном случае тип должен быть другим (std::optional<R>).

Ниже приведен полный пример компиляции.

#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 в качестве аргумента шаблона-шаблона.

...