Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting active value in std::visit without knowing which value is active

Tags:

c++

c++17

variant

I want to get the active value in a std::variant without knowing which one is active. I thought i could write a template visitor and use std::visit but it doesn't work.

#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 doesn't compile and throws:

error C2338: visit() requires the result of all potential invocations to have the same type and value category (N4741 23.7.7 [variant.visit]/2).

note: see reference to function template instantiation 'int std::visit&,0>(_Callable &&,std::variant &)' being compiled

So how to fix this?

edit: I want to use the obtained value maybe also for other so putting cout in the template isn't what im looking for.

like image 338
Sandro4912 Avatar asked Feb 14 '19 16:02

Sandro4912


2 Answers

Ask yourself the question:
What is the return type of std::visit if you don't know what part of the variant is active?

That is the question that the compiler must answer. And the answer can't be "it depends" - you (as in, the compiler) must decide on exactly one type at compile-time. The visit call cannot possibly return different types at runtime.

If you want to work with different types "at runtime", you must be in a function templated on the type you want to work with. In other words, there must be different functions (or function template instantiations) to handle the "write an int to cout" and "write a string to cout" cases. You cannot do this in the same (non-templated) function.

The straightforward solution here is thus to put the std::cout << into your templated visitor function - that's the point of visiting: Specifying what is supposed to happen in each case.

If you want to "use the obtained value maybe also for [some] other [purpose]", then that "other purpose" should also be part of the/a visitor. Only then can you have that "other purpose" handle the different cases at once (e.g. in a templated function). Otherwise you must decide at compile-time already which type shall be used - the compiler is not going to leave that choice open for later (run time).

like image 121
Max Langhof Avatar answered Sep 22 '22 16:09

Max Langhof


Return type of visitor function should be identical.

Create printer visitor instead:

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

And in your case, you can even have lambda:

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

    std::visit([](const auto& t){std::cout << t;}, v);   // expect "hello"
}
like image 42
Jarod42 Avatar answered Sep 22 '22 16:09

Jarod42