Can I pattern-match a type without writing a custom trait class?

Following the suggestion by several respondents, I made a re-usable 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;

…so that I can write:

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

This is just as many lines and declarations as the is_optional trait, but it's no longer a one-off; I can use the same trait for checking other kinds of templates (like variants and pairs). So now it feels like a useful addition to my project instead of a kluge.


Can I do the pattern matching on instances of std::optional without having to define the is_optional trait and its partial specialization?

Maybe using implicit deduction guides for std::optional?

I mean... something as

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

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

Explanation.

When R is std::optional<T> for some T type, std::optional{r} (for an r value of type R) should call the copy constructor and the resulting value should be of the same type R.

Otherwise, the type should be different (std::optional<R>).

The following is a full compiling example.

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

Anyway, I support the suggestion by super: create a more generic type-traits that receive std::option as template-template argument.