static_assert 类型属于 std::variant 接受的类型

static_assert that a type is among a std::variant's accepted types

在 C++17 中,如何在 constexpr 中验证类型属于变体的类型列表?

例如:

using MyVt = std::variant<int, float>;
static_assert( MyVt::has_type< bool >::value, "oops, forgot bool");

static_assert( mpl::has_key< MyVt::typelist, T >::value, "oops, forgot T");

当然在概念表达式中更有用,或者就像static_assert在模板函数中一样;限制可能接受的类型。

如果我们无法为此访问明确支持的标准元函数或元函数,是否可以使用涉及构造函数表达式的 SFINAE 破解检查?

基本解决方案使用fold expression (C++17) 和偏特化:

#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");

您可以使用模板模板参数使其更通用。这样,它也适用于 std::tuplestd::pair 和其他模板。但是,那些其他模板必须仅使用类型模板参数(例如,std::array 与下面示例中的模板模板参数 template<class...> class Tmpl 不匹配)。

template<class T, template<class...> class Tmpl, class... Ts>
struct IsContainedIn<T, Tmpl<Ts...>>
  : std::bool_constant<(... || std::is_same<T, Ts>{})>
{};

最后,this good C++17 answer to a C++11 question uses std::disjunction代替了折叠表达式。您可以将 std::disjunction 视为函数 any_of。这会启用 短路评估 (在编译时)。在这种情况下,它显示为

template<class T, template<class...> class Tmpl, class... Ts>
struct IsContainedIn<T, Tmpl<Ts...>>
  : std::disjunction<std::is_same<T, Ts>...>
{};

cppreference notes on std::disjunction 指出

[...]

The short-circuit instantiation differentiates disjunction from fold expressions: a fold expression like (... || Bs::value) instantiates every B in Bs, while std::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.

区别不大,但 Julius 的答案的替代方法可以通过声明 constexpr 函数和模板 constexpr 变量

template <typename T, template <typename...> class C, typename ... Ts>
constexpr auto isTypeInList (C<Ts...> const &)
    -> std::disjunction<std::is_same<T, Ts>...>;

template <typename T, typename V>
static constexpr bool isTypeInList_v 
   = decltype(isTypeInList<T>(std::declval<V>()))::value;

您可以按如下方式使用它们

using MyVt = std::variant<int, float>;

static_assert( isTypeInList_v<int, MyVt> );
static_assert( isTypeInList_v<double, MyVt> == false );

改进不大,但是...如果您还定义(非仅声明)isTypeInList() 函数

template <typename T, template <typename...> class C, typename ... Ts>
constexpr auto isTypeInList (C<Ts...> const &)
    -> std::disjunction<std::is_same<T, Ts>...>
 { return {}; } 

你也可以直接用它来检查对象

MyVt myVar {0};

static_assert( isTypeInList<int>(myVar) );

避免通过 decltype()

MyVt myVar {0};

static_assert( isTypeInList_v<int, decltype(myVar)> );

我喜欢 Boost.Mp11 的地方在于,似乎每个问题的答案都是 one-liner。在这种情况下,mp_contains:

static_assert(mp_contains<MyVt, bool>, "oops, forgot bool");

它是一个 header-only 独立库。这太棒了。请注意,这适用于 tuple 以及 variant


您可以通过以下方式大致了解该方法:

template <typename L, typename V> struct mp_contains_impl;
template <template<typename...> class L, typename... Ts, typename V>
struct mp_constaints_impl<L<Ts...>, V>
    : std::integral_constant<bool,
        (std::is_same_v<Ts, V> || ... )>
{ };

template <typename L, typename V>
using mp_contains = typename mp_contains_impl<L, V>::type;

一种完全不同的方法是创建一个可转换为完全您正在寻找的类型的类型,然后查看如果你可以从中构建你的变体:

template <typename T>
struct ConvertsTo {
    template <typename U,
        std::enable_if_t<std::is_same_v<T,U>, int> = 0>
    operator U() const;
};

template <typename V, typename T>
using variant_contains = std::is_constructible<V, ConvertsTo<T>>;

static_assert(variant_contains<std::variant<int, double>, int>::value);
static_assert(!variant_contains<std::variant<int, double>, bool>::value);