代码之家  ›  专栏  ›  技术社区  ›  rubenvb

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

  •  1
  • rubenvb  · 技术社区  · 7 年前

    在我看来,将可能是苹果或橙子的东西与可能是苹果、橙子或草莓的东西进行区分是很明确的。

    那么,为什么我不能呢 this ?

    #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 allows it (尽管std和boost变种并不完全相同)。我找不到缺陷报告或建议,所以我可能遗漏了一些C++中不允许的黑暗角落。

    1 回复  |  直到 7 年前
        1
  •  2
  •   Barry    7 年前

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

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

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

    目前,这是有效的,并且 w 持有 X 建造于 v .你想让这件事改变吗 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);