【问题标题】:Getting active value in std::visit without knowing which value is active在不知道哪个值处于活动状态的情况下在 std::visit 中获取活动值
【发布时间】:2019-07-08 18:00:52
【问题描述】:

我想在 std::variant 中获取活动值,但不知道哪个是活动的。我以为我可以编写一个模板访问者并使用 std::visit 但它不起作用。

#include <variant>
#include <string>
#include <iostream>

struct Visit_configuration {
    template<typename Data_type>
    Data_type operator()(Data_type& t) const
    {
        return t;
    }
};

int main()
{
    std::variant<int, std::string> v;
    v = "hello";

    std::cout << std::visit(Visit_configuration(), v);   // expect "hello"
    std::cin.get();
}

MSVC 不编译并抛出:

错误 C2338: visit() 需要所有潜在调用的结果 具有相同的类型和值类别(N4741 23.7.7 [variant.visit]/2)。

注意:参见函数模板实例化 'int std::visit&,0>(_Callable &&,std::variant &)' 正在编译

那么如何解决这个问题?

编辑:我想将获得的值也用于其他值,因此将 cout 放入模板中并不是我想要的。

【问题讨论】:

  • 虽然您和我都可以看到v 将始终包含std::string,并且一个好的静态分析工具也可以做出该决定,但这超出了编译器所需的能力范围推断。值得注意的是,v 不是 constexpr,并且编译器在编译时不理解 v 包含的内容,而不是依赖于用户输入。所以无法确定std::visit的返回类型。
  • 你应该让访问者operator() 输出值,而不是试图从访问者那里返回它以便调用者可以输出它。对于examplestd::visit([](auto&amp;&amp; arg){std::cout &lt;&lt; arg;}, v);

标签: c++ c++17 variant


【解决方案1】:

问自己一个问题:
如果您不知道variant 的哪一部分处于活动状态,std::visit 的返回类型是什么?

这是编译器必须回答的问题。答案不能是“视情况而定”——您(如编译器)必须在编译时决定恰好一种类型visit 调用不可能在运行时返回不同的类型。

如果你想在“运行时”使用不同的类型,你必须在你想使用的类型模板化的函数中。换句话说,必须有不同的函数(或函数模板实例化)来处理“将 int 写入cout”和“将字符串写入cout”的情况。您不能在同一个(非模板化)函数中执行此操作。

因此,这里直接的解决方案是将std::cout &lt;&lt; 放入您的模板访问者函数中——这就是访问的重点:指定每种情况下应该发生的事情。

如果您想“将获得的值用于 [某些] 其他 [目的]”,那么“其他目的”也应该是访问者的一部分。只有这样,您才能让“其他目的”同时处理不同的情况(例如,在模板化函数中)。否则,您必须在编译时已经决定应该使用哪种类型 - 编译器不会让该选择留待以后(运行时)。

【讨论】:

    【解决方案2】:

    访问者函数的返回类型应该相同。

    改为创建打印机访问者:

    struct PrinterVisitor {
        template<typename T>
        void operator()(const T& t) const
        {
            std::cout << t;
        }
    };
    
    int main()
    {
        std::variant<int, std::string> v;
        v = "hello";
    
        std::visit(PrinterVisitor{}, v);   // expect "hello"
    }
    

    在你的情况下,你甚至可以拥有 lambda:

    int main()
    {
        std::variant<int, std::string> v;
        v = "hello";
    
        std::visit([](const auto& t){std::cout << t;}, v);   // expect "hello"
    }
    

    【讨论】:

    • 好的,但是如果我想访问 std::cout 以外的其他值怎么办?有办法吗?
    • 请解释为什么/如何此代码与有问题的代码不同。
    • @Sandro4912 将std::ostream&amp; 构造函数添加到PrinterVisitor 似乎很简单。
    • @RobertAndrzejuk:Visitor operator() 的返回类型应该是相同的,而 OP 则不是这样。
    • @FrançoisAndrieux 那么与void 有什么不同吗?然后visit返回结果?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-07
    • 2012-08-09
    • 2016-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多