我可以将 std::variant<Ts...> 分配给/从 std::variant<Ts..., Ys...> 分配/构造一个 std::variantTs... 吗?

Can I assign/construct an std::variant<Ts...> to/from an std::variant<Ts..., Ys...>?

本文关键字:std Ts gt 分配 lt variant variantTs 一个 我可以 Ys      更新时间:2023-10-16

在我看来,将苹果或橙子的东西分配给可能是苹果、橙子或草莓的东西是明确的。

那为什么,我不能这样做呢?

#include <variant>
int main()
{
    std::variant<int> v1{42};
    std::variant<int, char> v2{v1}; // copy construction
    v2 = v1; // copy assignment
    v2 = std::move(v1); // move assignment
    std::variant<int, char> v3{std::move(v2)}; // move construction
}

从概念上讲,这似乎没问题。甚至 boost::variant 也允许它(即使 std 和 boost variant 不完全是同一个野兽)。我找不到缺陷报告或提案,所以我可能错过了一些不允许C++黑暗角落的原因。

无论出于何种原因,标准库中都不支持它。 std::variant是一个非常漫长、非常有争议的过程。也许这个特定方面在任何人的名单上都不高?

添加这样的转换构造函数和转换赋值运算符的唯一可能的技术问题是与当前构造函数的奇怪病理交互。现在,variant<Ts...>有一个构造函数,它试图选择一个Ts T&&。这有可能发生冲突,你必须回答你想在这里发生什么的问题:

struct X { X(variant<int>); };
variant<int>    v = 42;
variant<int, X> w = v;

目前,这是有效的,并且w持有由v构造的X。您是否希望这种情况改变并w持有int 42

<小时 />

现在,您只需要手动执行此操作:

template <typename To, typename From>
To variant_cast(From&& from) {
    return std::visit(
        [](auto&& elem) { return To(std::forward<Elem>(elem)); },
        std::forward<From>(from));
}

你不会得到那么好的语法(上面的实现对SFINAE不友好),但它可以完成工作:

using V2 = variant<int, char>;
auto v2 = variant_cast<V2>(v1);
v2 = variant_cast<V2>(v1);
v2 = variant_cast<V2>(std::move(v1));
// at least this one is stil easy :-)
auto v3 = std::move(v2);