在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破解检查吗?
答案 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::tuple
,std::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);