你如何编写一个函数模板来确定两个任意变体是否持有相同的类型?

问题描述 投票:0回答:2

考虑以下函数来确定相同变体类型的变量是否持有相同的类型:

#include <variant>
#include <iostream>

template <typename... Ts>
bool hold_same_types(const std::variant<Ts...>& v1, const std::variant<Ts...>& v2) {
    return ((std::holds_alternative<Ts>(v1) && std::holds_alternative<Ts>(v2)) || ...);
}

struct foo {};
struct bar {};
struct quux {};
struct mumble {};

using var1 = std::variant<foo, bar, quux>;

int main()
{
    var1 b1 = bar{};
    var1 b2 = bar{};
    var1 q = quux{};

    std::cout << "(b1 , b2)  => " << hold_same_types(b1, b2) << "\n";
    std::cout << "(b1 , q)  => " << hold_same_types(b1, q) << "\n";
}

似乎没有简单的方法来扩展

hold_same_types
来处理异构变体。例如,以下不起作用

//...
using var1 = std::variant<foo, bar, quux>;
using var2 = std::variant<bar, quux, mumble>;

template <typename... Ts, typename... Us>
bool hold_same_types(const std::variant<Ts...>& v1, const std::variant<Us...>& v2) {
    return ((std::holds_alternative<Ts>(v1) && std::holds_alternative<Ts>(v2)) || ...);
}

int main()
{
    var1 b1 = bar{};
    var2 m = mumble{};

    std::cout << "(b1 , m)  => " << hold_same_types(b1, m) << "\n";
}

因为

  1. 你不能像那样使用多个参数包。

  2. 即使您可以解决这个问题,如果

    Ts...
    包含一个不在
    Us...
    中的类型,折叠表达式也不会编译。

也就是说,如果 T 不是

std::holds_alternative<T>(some_variant)
中的替代项,
some_variant
将不会返回 false,它甚至不会编译。

我尝试了一些更复杂的方法,但我无法让它们起作用。我遇到的主要问题是通常无法使用两个参数包。

c++ templates variadic-templates std-variant
2个回答
2
投票

对于相同类型的两个变体,您可以只比较它们的

.index()
.

对于不同的变体,您可以使用

std::visit
。它一次可以接受多个变体:

template <typename A, typename B>
constexpr bool hold_same_type(const A &a, const B &b)
{
    return std::visit(
        [](const auto &a, const auto &b){return std::is_same_v<decltype(a), decltype(b)>;},
        a, b
    );
}

这会实例化 lambda M*N 次,这对于大型变体来说可能是个坏主意,也可能不是。

作为替代方案,您可以编写一个函数,为存储在单个变体中的类型返回

const std::type_info &
(再次使用
std::visit
),然后比较两个变体的结果。这是留给读者的练习。


0
投票
  1. 您可以将
    hold_same_types
    的模板参数更改为
    template <typename Variant, typename... Ts>
    ,因为它足以检查一个变体类型的所有类型与另一个变体。如果另一个变体没有类型,它肯定不会旧相同的类型。
  2. 您可以编写自己的
    my_holds_alternative
    实现,如果类型未包含在变体中,则返回
    false
    ,而不是失败的
    static_assert
#include <variant>
#include <iostream>

template <typename Variant, typename T, size_t... I>
constexpr bool has_type_impl(std::index_sequence<I...>)
{
    return (std::is_same_v<std::variant_alternative_t<I, Variant>, T> || ...);
}

template <typename Variant, typename T>
constexpr bool has_type()
{
    return has_type_impl<Variant, T>(std::make_index_sequence<std::variant_size_v<Variant>>());
}

template <typename T, typename Variant>
constexpr bool my_holds_alternative(Variant const& v)
{
    if constexpr (has_type<Variant, T>())
    {
        return std::holds_alternative<T>(v);
    } else {
        return false;
    }
}

template <typename Variant, typename... Ts>
constexpr bool hold_same_types(const Variant& v1, const std::variant<Ts...>& v2)
{
    return ((my_holds_alternative<Ts>(v1) && my_holds_alternative<Ts>(v2)) || ...);
}

struct foo {};
struct bar {};
struct quux {};
struct mumble {};

using var1 = std::variant<foo, bar, quux>;
using var2 = std::variant<bar, quux, mumble>;

int main()
{
    var1 b1 = bar{};
    var1 b2 = bar{};
    var1 q = quux{};

    std::cout << "(b1 , b2)  => " << hold_same_types(b1, b2) << "\n";
    std::cout << "(b1 , q)  => " << hold_same_types(b1, q) << "\n";

    var2 m = mumble{};

    std::cout << "(b1 , m)  => " << hold_same_types(b1, m) << "\n";
}
(b1 , b2)  => 1
(b1 , q)  => 0
(b1 , m)  => 0

https://godbolt.org/z/Ej1adsfqM

© www.soinside.com 2019 - 2024. All rights reserved.