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
CodePudding user response:
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 ofvalue
. 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]);
}
CodePudding user response:
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';
}
CodePudding user response:
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.