static_assert, что тип относится к числу принятых типов std :: option - PullRequest
0 голосов
/ 12 декабря 2018

В 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 голосов
/ 12 декабря 2018

Совершенно другой подход заключается в создании типа, который только может быть преобразован в точно тип, который вы ищете, и посмотрите, сможете ли вы создать свой вариант из него:

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);
0 голосов
/ 12 декабря 2018

Не большая разница, но альтернативой ответу Юлиуса может быть использование одной и той же проверки (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()

MyVt myVar {0};

static_assert( isTypeInList_v<int, decltype(myVar)> );
0 голосов
/ 12 декабря 2018

Что мне нравится в 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;
0 голосов
/ 12 декабря 2018

Базовое решение использует выражение сгиба (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>{})>
{};

Наконец, это хороший C++ 17 ответ на вопрос C ++ 11 использует 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 на std::disjunction утверждают, что

[...]

создание экземпляров при коротком замыкании отличает дизъюнкцию от выражений сгиба: выражение сгиба, подобное (... || Bs::value), создает каждый B в Bs, в то время как std::disjunction_v<Bs...> останавливает создание экземпляров после определения значения.Это особенно полезно, если более поздний тип является дорогостоящим для создания экземпляра или может вызвать серьезную ошибку при создании экземпляра с неправильным типом.

...