Since C++20 concepts aren't standardized yet, I'm using static_assert
as a makeshift concept check, to provide helpful error messages if a type requirement isn't met. In this particular case, I have a function which requires that a type is callable before getting its result type:
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...>;
// ...
}
In addition, I require that the callable's result must be some kind of std::optional
, but I don't know what type the optional will hold, so I need to get that type from it:
using R = // ...
using T = typename R::value_type; // std::optional defines a value_type
However, this will fail if type R
doesn't have a value_type
, e.g. if it's not a std::optional
as expected. I'd like to have a static_assert
to check for that first, with another nice error message if the assertion fails.
I could check for an exact type with something like std::is_same_v
, but in this case I don't know the exact type. I want to check that R
is some instance of std::optional
, without specifying which instance it must be.
One way to do that is with a helper trait:
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;
…and then I can write:
static_assert(is_optional_v<R>, "Function's result must be an optional");
That works, but it seems a little awkward to pollute my namespace with a helper trait just for a one-off check like this. I don't expect to need is_optional
anywhere else, though I can imagine possibly ending up with other one-off traits like is_variant
or is_pair
too.
So I'm wondering: is there a more concise way to do this? Can I do the pattern matching on instances of std::optional
without having to define the is_optional
trait and its partial specialization?
constexpr bool is_optional_v = is_template_of<std::optional, T>::value;
and could be reused for pair/variant with one liners. – Rompish