Заставить свойство типа работать для всех производных типов

У меня есть черта типа и концепция, которая проверяет, может ли std::variant содержать заданный тип T. Теперь у меня есть тип variant2, производный от std::variant, и я хочу использовать этот признак типа с новым типом variant2. Как я могу сделать это элегантно?

Это не работает (Демо):

#include <variant>
#include <string>
#include <iostream>
#include <concepts>
#include <type_traits>

template<typename T, typename Variant>
struct variant_type;

template<typename T, typename... Args>
struct variant_type<T, std::variant<Args...>> 
    : public std::disjunction<std::is_same<T, Args>...> {};

template<typename T, typename Variant>
concept is_variant_type = variant_type<T, Variant>::value;

template <typename... Ts>
struct variant2 : public std::variant<Ts...> {
};

variant2<std::monostate, int, bool, std::string> var;

int main() {
    using T = std::string;

    if constexpr (is_variant_type<T, decltype(var)>) {
        std::cout << "Worked!" << std::endl;
    }
}

«Работал» никогда не появляется на экране, что очевидно, потому что чертой типа по умолчанию является SFINAED.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
0
74
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Поскольку variant2 наследует std::variant, вы можете извлечь его базовый тип variant с помощью следующей вспомогательной функции.

template<typename... Args>
auto as_variant(const std::variant<Args...>&) -> std::variant<Args...>;

template<typename T>
struct to_variant;

template<typename T>
  requires requires (const T& t) { as_variant(t); }
struct to_variant<T> {
  using type = decltype(as_variant(std::declval<const T&>()));
};

Затем немного измените концепцию is_variant_type

template<typename T, typename Variant>
concept is_variant_type = 
  variant_type<T, typename to_variant<Variant>::type>::value;

Или, проще говоря, просто определите is_variant_type напрямую как

template<typename T, typename Variant>
concept is_variant_type = requires (const Variant& var) {
  []<typename... Args>(const std::variant<Args...>&) 
    requires (std::same_as<T, Args> || ...)
  { }(var);
};

Демо

Ответ принят как подходящий

Вы можете просто исправить свою специализацию, указав, что ваш шаблон должен наследоваться от std::variant

#include <concepts>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>

template <typename T, typename Variant>
struct variant_type;

template <typename T, template <typename...> typename Var, typename... Args>
struct variant_type<T, Var<Args...>>
    : public std::conjunction<
          std::disjunction<std::is_same<T, Args>...>,
          std::is_base_of<std::variant<Args...>, Var<Args...>>> {};

template <typename T, typename Variant>
concept is_variant_type = variant_type<T, Variant>::value;

template <typename... Ts>
struct variant2 : public std::variant<Ts...> {};

variant2<std::monostate, int, bool, std::string> var;

int main() {
    using T = std::string;

    if constexpr (is_variant_type<T, decltype(var)>) {
        std::cout << "Worked!" << std::endl;
    }
}

Я просто добавил союз, чтобы добиться этого, и мы закончили.
В прямом эфире
[EDIT] извините, это немного сложнее: я добавил параметр шаблона шаблона, который подразумевает, что ваш унаследованный класс имеет точно такие же параметры шаблона, как и базовый, что может быть ограничительным.

Я думаю, что этот ответ является более общим, поскольку он не делает предположений о параметрах унаследованного шаблона класса, он просто указывает, что он может быть преобразован в вариант и получит свои параметры. <br> Возможно, это улучшение было бы смешайте оба ответа и убедитесь, что класс variant2 действительно унаследован от std::variant, тем самым исключая класс, который можно было бы неявно преобразовать в ваш std::variant (хотя я точно не знаю, как это сделать, не зная Args...).

Oersted 12.06.2023 17:21

было бы очень легко настроить его, используя std::is_convertible вместо std::is_base_of, и я также нахожу это решение предпочтительным. Я бы также изменил имена variant_type и is_variant_type на is_variant_type и is_variant_type_v соответственно, чтобы следовать стандартным соглашениям об именах признаков.

Jan Schultke 12.06.2023 18:13

@JanSchultke Какое имя вы бы предложили для этой концепции?

glades 13.06.2023 07:01

Понятия @glades в стандартной библиотеке обычно не имеют суффикса и не начинаются с is_, поэтому понятие должно быть просто variant_type или, возможно, variant_type_in.

Jan Schultke 13.06.2023 12:05

@JanSchultke Действительно, я перепутал, спасибо, что указали на это :)

glades 13.06.2023 15:49

Другие вопросы по теме

Похожие вопросы