提问人:glades 提问时间:6/12/2023 更新时间:6/13/2023 访问量:94
使类型特征适用于所有派生类型
Making type trait work for all derived types
问:
我有一个类型特征和概念,可以检查是否可以容纳给定的类型。现在我有一个派生自 的类型,我想将该类型特征与新类型一起使用。我怎样才能优雅地完成这项工作?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。
答:
2赞
康桓瑋
6/12/2023
#1
由于 inherits ,您可以通过以下帮助函数提取其基类型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);
};
5赞
Oersted
6/12/2023
#2
你可以通过表达你的模板必须继承来修复你的专业化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;
}
}
我只是添加了一个连词来实现这一点,我们就完成了。
Live
[EDIT] 对不起,它只是有点复杂:我添加了一个模板模板参数,这意味着您继承的类具有与基本类完全相同的模板参数,这可能是限制性的。
评论
0赞
Oersted
6/12/2023
我认为这个答案更通用,因为它没有对继承的类模板参数做出任何假设,它只是表示它可以转换为变体并将检索其参数><。 也许这种改进是混合答案和强制执行该类确实是从中继承的,从而排除了可以隐式转换为 ur 的类(但我不知道没有确切的操作知道)。variant2
std::variant
std::variant
Args...
0赞
Jan Schultke
6/13/2023
通过使用代替 来调整它非常容易,我也会发现该解决方案更可取。我还会分别将 和 的名称更改为 和 ,以遵循标准的特征命名约定。std::is_convertible
std::is_base_of
variant_type
is_variant_type
is_variant_type
is_variant_type_v
0赞
glades
6/13/2023
@JanSchultke 那么你会为这个概念建议什么名字呢?
0赞
Jan Schultke
6/13/2023
标准库中的@glades概念通常没有任何后缀,也不以 开头,因此概念应该只是 ,或者 。is_
variant_type
variant_type_in
0赞
glades
6/13/2023
@JanSchultke 确实,我把它扭曲了,谢谢你指出这一点:)
评论