У меня есть черта типа и концепция, которая проверяет, может ли 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.
Поскольку 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] извините, это немного сложнее: я добавил параметр шаблона шаблона, который подразумевает, что ваш унаследованный класс имеет точно такие же параметры шаблона, как и базовый, что может быть ограничительным.
было бы очень легко настроить его, используя std::is_convertible
вместо std::is_base_of
, и я также нахожу это решение предпочтительным. Я бы также изменил имена variant_type
и is_variant_type
на is_variant_type
и is_variant_type_v
соответственно, чтобы следовать стандартным соглашениям об именах признаков.
@JanSchultke Какое имя вы бы предложили для этой концепции?
Понятия @glades в стандартной библиотеке обычно не имеют суффикса и не начинаются с is_
, поэтому понятие должно быть просто variant_type
или, возможно, variant_type_in
.
@JanSchultke Действительно, я перепутал, спасибо, что указали на это :)
Я думаю, что этот ответ является более общим, поскольку он не делает предположений о параметрах унаследованного шаблона класса, он просто указывает, что он может быть преобразован в вариант и получит свои параметры. <br> Возможно, это улучшение было бы смешайте оба ответа и убедитесь, что класс
variant2
действительно унаследован отstd::variant
, тем самым исключая класс, который можно было бы неявно преобразовать в вашstd::variant
(хотя я точно не знаю, как это сделать, не знаяArgs...
).