In C++17 how can one verify in a constexpr
that a type belongs to the typelist of a variant ?
e.g:
using MyVt = std::variant<int, float>;
static_assert( MyVt::has_type< bool >::value, "oops, forgot bool");
or
static_assert( mpl::has_key< MyVt::typelist, T >::value, "oops, forgot T");
Of course more useful in concept expressions, or just as static_assert
in a template function; to restrict the possible types accepted.
If we don't have access to an explicitly supported standard metafunction or metalist for this, would it be possible to hack a check using SFINAE involving a constructor expression ?
The basic solution uses a fold expression (C++17) and partial specialization:
#include <type_traits>
#include <variant>
template<class T, class TypeList>
struct IsContainedIn;
template<class T, class... Ts>
struct IsContainedIn<T, std::variant<Ts...>>
: std::bool_constant<(... || std::is_same<T, Ts>{})>
{};
using MyVt = std::variant<int, float>;
static_assert(IsContainedIn<bool, MyVt>::value, "oops, forgot bool");
You can make it more generic by using a template template parameter. This way, it also works for std::tuple
, std::pair
, and other templates. Those other templates must use only type template parameters, though (e.g., std::array
does not match the template template parameter template<class...> class Tmpl
in the example below).
template<class T, template<class...> class Tmpl, class... Ts>
struct IsContainedIn<T, Tmpl<Ts...>>
: std::bool_constant<(... || std::is_same<T, Ts>{})>
{};
Finally, this good C++17 answer to a C++11 question uses std::disjunction
instead of a fold expression. You can think of std::disjunction
as the functional any_of
. This enables short-circuit evaluation (at compile time). In this case it reads
template<class T, template<class...> class Tmpl, class... Ts>
struct IsContainedIn<T, Tmpl<Ts...>>
: std::disjunction<std::is_same<T, Ts>...>
{};
The cppreference notes on std::disjunction
state that
[...]
The short-circuit instantiation differentiates disjunction from fold expressions: a fold expression like
(... || Bs::value)
instantiates everyB
inBs
, whilestd::disjunction_v<Bs...>
stops instantiation once the value can be determined. This is particularly useful if the later type is expensive to instantiate or can cause a hard error when instantiated with the wrong type.