使类型特征适用于所有派生类型

Making type trait work for all derived types

提问人:glades 提问时间:6/12/2023 更新时间:6/13/2023 访问量:94

问:

我有一个类型特征和概念,可以检查是否可以容纳给定的类型。现在我有一个派生自 的类型,我想将该类型特征与新类型一起使用。我怎样才能优雅地完成这项工作?std::variantTvariant2std::variantvariant2

这不起作用(演示):

#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。

C++ 类型特征 C++-Concepts 基类

评论


答:

2赞 康桓瑋 6/12/2023 #1

由于 inherits ,您可以通过以下帮助函数提取其基类型variant2std::variantvariant

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 的类(但我不知道没有确切的操作知道)。variant2std::variantstd::variantArgs...
0赞 Jan Schultke 6/13/2023
通过使用代替 来调整它非常容易,我也会发现该解决方案更可取。我还会分别将 和 的名称更改为 和 ,以遵循标准的特征命名约定。std::is_convertiblestd::is_base_ofvariant_typeis_variant_typeis_variant_typeis_variant_type_v
0赞 glades 6/13/2023
@JanSchultke 那么你会为这个概念建议什么名字呢?
0赞 Jan Schultke 6/13/2023
标准库中的@glades概念通常没有任何后缀,也不以 开头,因此概念应该只是 ,或者 。is_variant_typevariant_type_in
0赞 glades 6/13/2023
@JanSchultke 确实,我把它扭曲了,谢谢你指出这一点:)