考虑以下函数来确定相同变体类型的变量是否持有相同的类型:
#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";
}
因为
你不能像那样使用多个参数包。
即使您可以解决这个问题,如果
Ts...
包含一个不在 Us...
中的类型,折叠表达式也不会编译。
也就是说,如果 T 不是
std::holds_alternative<T>(some_variant)
中的替代项,some_variant
将不会返回 false,它甚至不会编译。
我尝试了一些更复杂的方法,但我无法让它们起作用。我遇到的主要问题是通常无法使用两个参数包。
.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
),然后比较两个变体的结果。这是留给读者的练习。
hold_same_types
的模板参数更改为 template <typename Variant, typename... Ts>
,因为它足以检查一个变体类型的所有类型与另一个变体。如果另一个变体没有类型,它肯定不会旧相同的类型。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