在std :: visit中获取活动值而不知道哪个值是活动的

时间:2019-02-14 16:31:25

标签: c++ c++17 variant

我想在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放到模板中并不是我想要的。

2 个答案:

答案 0 :(得分:7)

问自己一个问题:
如果您不知道std::visit的哪一部分有效,那么variant的返回类型是什么?

这是编译器必须回答的问题。答案不可能是“取决于”的-您(例如,编译器)必须在编译时确定一种类型visit调用在运行时可能无法返回不同的类型。

如果要“在运行时”使用其他类型,则必须处于要使用的类型作为模板的函数中。换句话说,必须有不同的功能(或功能模板实例化)来处理“将整数写入cout”和“将字符串写入cout”的情况。您不能使用相同的(非模板化)功能来做到这一点。

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

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

答案 1 :(得分:3)

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

改为创建打印机访问者:

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"
}