简体   繁体   中英

How could return map<int, variant>'s value by a function?

I want to have a map to save different types of value, so I have a map like

std::map<int, std::variant<int, std::string>> m ={{1,1},{2,"asd"}};

And Now I want to design a function to get the value by its key like

auto get(int key) {
    ...
    return value;
}

That's to say what I want is like

get(1) -> 1
get(2) -> "asd"

So is it possible? If so what's the exact solution?


ADDED :

Yes, as for the purpose of the design, I want to save config datas reading from config files.

And after reading I need to make some type changing or data transform. Such as in config files like

a=1
b=asd

And after reading it, I want to save it as m["a"]=int(3) ---->refers a=1 and needs to plus 2 after reading its actual data 1 m["b"]=std::string("asdasd") ---->refers b=asd and needs to double it

So I think it will be easier if there is an interface function to get the exact value by the key

Two possibilities:

A) You want to return either int or std::string depending on the index passed to the function.

Not possible. A function has one return type. Also auto is not magic. If you cannot write the actual type, then the compiler can't either.

B) You want to return the mapped_type of the map.

You can return std::variant<int, std::string> from the function.


Your get can't do much "better" than std::variant::get . With std::variant::get you need to specify at compile time what type you want to retrieve. And there is no way around that if you want to get either int or std::string .


There might be better ways to solve your actual issue, the one for which you thought auto get(int key) was the solution. A comment (by Eljay) mentions std::visit . There you can see a couple of examples of how to deal with variants.

You cannot exactly design a function with this syntax. Just imagine this:

int key = 0;
if (rand() % 2) {
    key = 1;
} else {
    key = 2;
}

auto value = get(key);

Here's the riddle:

Please tell me the return type of get and the type of value . There can be only one answer and you can only answer with a single type that will remain unchanging no matter how much time I ask you this question.

If you can't answer, well, the compiler cannot either.

However, it doesn't mean you can't design something similar that will do what you need.

You could just return the variant. This might not be exactly what you're looking for, but it's worth noting since it doesn't change any of the input of the get function.

You can also just send the expected type:

get<int>(key)

The implementation would look like this:

template<typename T>
auto get(int key) -> T {
    return std::get<int>(m[key]);
}

If you cannot know the expected type, then you could just send a visitor:

get(key, [](auto value) -> std::size_t {
    if constexpr (std::same_as<int, decltype(value)>) {
        // do stuff with value as an int since it's a int here
        return value + 1;
    } else {
        // do stuff with value as a string since it's a string here
        return value.size(); 
    }
});

The implementation would look like this:

auto get(int key, auto visitor) -> decltype(auto) {
    return std::visit(visitor, m[key]);
}

If I understand the requirements, you could return a proxy object from get that keeps a reference to the variant and depending on what you do with the proxy object, it can adapt. Example:

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

struct Foo {
    using variant = std::variant<int, std::string>;

    // the proxy object
    struct proxy {
        // assignment:
        template<class T>
        variant& operator=(T&& value) {
            *data = std::forward<T>(value);
            return *data;
        }

        // for static_cast to the wanted type
        explicit operator int& () { return std::get<int>(*data); }
        explicit operator std::string& () { return std::get<std::string>(*data); }

        variant* data;
    };

    proxy get(int key) {
        // return the proxy object
        return proxy{&m[key]};
    }

    std::map<int, std::variant<int, std::string>> m = {{1,1}, {2,"asd"}};
};

And it could be used like this:

int main() {
    Foo f;
    
    // you need to know what it stores:
    std::cout << static_cast<int>(f.get(1)) << '\n';
    std::cout << static_cast<std::string>(f.get(2)) << '\n';

    // assigning is simple:
    f.get(1) = "hello world"; // was int, now std::string
    f.get(2) = 2;             // was std::string, now int

    std::cout << static_cast<std::string>(f.get(1)) << '\n';
    std::cout << static_cast<int>(f.get(2)) << '\n';
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM