У меня есть класс шаблона, который принимает std::variant
в качестве параметра шаблона:
template<class T>
class X
{
};
X<std::variant<int, float>> x;
Я хочу добавить ограничение на получение T
из std::variant
.
Затем я хочу извлечь параметры шаблона этого варианта, чтобы использовать их внутри класса.
Так:
template<class T, std::enable_if_t<
std::is_base_of_v<std::variant<T::_Types>, T>, // _Types = int, float
bool> = true>
class X
{
std::tuple<T::_Types> t; // std::tuple<int, float>
};
X<std::variant<int, float>> x;
Проблема в том, что я не могу получить эти параметры int, float
из типа variant
.
Есть ли способ сделать это на С++ 17?
Этот вариант происходит от типа возвращаемого значения функции.
Из-за этого я не могу вместо этого изменить его на что-то вроде параметров варианта (чтобы взять int, float
напрямую).
Признаки типа types_list
предоставляют специализированный кортеж для различных типов, составляющих специализацию шаблонного класса T
, в противном случае предоставляется пустой кортеж. Для простоты используется стандарт std::tuple
.
Для реализации этого метода достаточно добавить частичную специализацию, которая принимает универсальный класс и список его шаблонных аргументов.
Пример:
template <typename>
struct types_list
{ using type = std::tuple<>; };
template <template <typename...> typename T, typename ...Args>
struct types_list<T<Args...>>
{ using type = std::tuple<Args...>; };
template <typename T>
using types_list_t = typename types_list<T>::type;
С другой стороны, признак типа is_variant
проверяет, является ли тип T
специализацией std::variant
.
Для реализации условия достаточно добавить частичную специализацию, которая принимает специализацию std::variant
.
Пример:
template <typename>
struct is_variant
: std::false_type {};
template <typename ...Args>
struct is_variant<std::variant<Args...>>
: std::true_type {};
template <typename T>
inline constexpr bool is_variant_v = is_variant<T>::value;
Таким образом, класс можно перереализовать следующим образом.
template <typename T, typename = std::enable_if_t<is_variant_v<T>>>
class X
{
types_list_t<T> t;
};
Итак, как я могу использовать это в своем примере? Мне нужен класс шаблона X<T>
, который принимает variant<int, float>
как T
и может получить доступ к пакету параметров int, float
внутри. Можете ли вы привести пример?
Как вы принимаете типы, производные от std::variant
, и как извлекаете аргументы шаблона std::variant
.
@HolyBlackCat, в комментарии ОП просит ограничить класс, чтобы он принял специализацию std::variant
как T. Таким образом, я предоставляю частный случай экспериментальной черты типа is_specialization_of
.
Моя вина, упустил из виду этот момент.
Вы можете обобщить types_list
, чтобы преобразовать его в любой другой шаблон: template <typename, template <typename...> typename T> struct types_list; template <template <typename...> typename In, typename ...Args, template <typename...> typename Out> struct types_list<In<Args...>, Out> { using type = Out<Args...>; };
@Калет, ты прав. Я также мог бы обобщить черту типа is_variant
на is_specialization_of
, но предпочел быть ближе к потребностям ОП.
Чтобы получить родителя std::variant
из производного от него класса (если я вас правильно понимаю, вы тоже хотите принимать производные классы), сделайте следующее:
template <typename ...P>
std::variant<P...> &VariantIdentity(std::variant<P...> &);
template <typename T>
using ToBaseVariant = std::remove_reference_t<decltype((VariantIdentity)(std::declval<T &>()))>;
Затем вы можете извлечь аргументы из варианта, используя частичную специализацию. Например. базового класса X
или, как предполагает другой ответ, совершенно отдельного класса.
template <typename T>
class XBase;
template <typename ...P>
class XBase<std::variant<P...>>
{
std::tuple<P...> t;
};
template <typename T>
class X : XBase<ToBaseVariant<T>>
{
using XBase<ToBaseVariant<T>>::XBase;
};
Как реализована функция VariantIdentity()
? Возвращает ли он входную ссылку без изменений?
@LoS У него нет реализации. Ему это не нужно, потому что он вызывается только внутри decltype
.
Наследование от std::variant — не очень хорошая идея.