static_assert一个类型在std :: variant的可接受类型之中

时间:2018-12-12 08:28:19

标签: c++ metaprogramming c++17 variant

在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破解检查吗?

4 个答案:

答案 0 :(得分:4)

基本解决方案使用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使用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声明

  

[...]

     

短路实例将折叠表达式与折叠表达式区分开来:像(... || Bs::value)这样的折叠表达式实例化B中的每个Bs,而std::disjunction_v<Bs...>一旦值可以为,就停止实例化。决心。如果后面的类型实例化很昂贵,或者在使用错误类型实例化时可能导致硬错误,则这特别有用。

答案 1 :(得分:1)

通过使用{{1}的声明,相同的检查(std::bool_constant<(... || std::is_same<T, Ts>{}),或者更好的std::disjunction<std::is_same<T, Ts>...>)可以使相同的检查成为朱利叶斯答案的另一种选择}函数和模板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()

答案 2 :(得分:1)

我喜欢Boost.Mp11的原因在于,似乎每个问题的答案都是一线的。在这种情况下,mp_contains

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

这是仅标头的独立库。太棒了。请注意,这既适用于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;

答案 3 :(得分:1)

一种完全不同的方法是创建一个类型,该类型仅 可转换为完全您要查找的类型,并查看是否可以从中构造变体:

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