如何通过函数返回 map<int, variant> 的值?

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

提问人:f1msch 提问时间:1/27/2023 最后编辑:f1msch 更新时间:1/27/2023 访问量:179

问:

我想要一个地图来保存不同类型的值,所以我有一个像这样的地图

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

现在我想设计一个函数来通过它的键来获取值,比如

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

也就是说,我想要的样子

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

那么这可能吗?如果是这样,确切的解决方案是什么?


新增内容

是的,就设计目的而言,我想保存从配置文件中读取的配置数据。

阅读后,我需要进行一些类型更改或数据转换。 例如在配置文件中,例如

a=1
b=asd

看完后,我想把它保存为 m[“a”]=int(3) ---->引用并需要在读取其实际数据后加 2 m[“b”]=std::string(“asdasd”) ---->引用并需要将其加倍a=11b=asd

所以我认为如果有一个接口函数通过键获取确切的值会更容易

C++ 字典 C++17 变体

评论

8赞 HolyBlackCat 1/27/2023
这些类型在编译时是固定的。函数必须返回一个变体。
0赞 463035818_is_not_an_ai 1/27/2023
除非您想要并返回不同的类型,否则我不明白这个问题。这些值的类型因此可以返回get(1)get(2)std::variant<int,std::string>>
1赞 Sam Varshavchik 1/27/2023
C++ 不以这种方式工作。您的函数只能返回 .唯一的替代方法是将函数作为模板,该模板将可调用对象作为参数提供给 。std::variant<int, std::string>std::visit
1赞 FRINTSO 1/27/2023
最好找到另一种方法来实现你的目标。返回不同的类型可能很麻烦,而且效率非常低。
2赞 rustyx 1/27/2023
一句智慧的话:不要这样做。为了获得最佳性能,请使用 2 个单独的地图;一对一为 .intstring

答:

2赞 463035818_is_not_an_ai 1/27/2023 #1

两种可能性:

A) 您希望返回其中之一,或者取决于传递给函数的索引。intstd::string

不可能。函数具有一种返回类型。也不是魔术。如果无法编写实际类型,则编译器也无法编写。auto

B) 您要返回地图。mapped_type

您可以从函数返回。std::variant<int, std::string>


你不能比 .您需要在编译时指定要检索的类型。如果你想得到任何一个或.getstd::variant::getstd::variant::getintstd::string


可能有更好的方法来解决您的实际问题,您认为是解决方案的方法。一条评论(由 Eljay 撰写)提到了 std::visit。在那里,您可以看到如何处理变体的几个示例。auto get(int key)

2赞 Guillaume Racicot 1/27/2023 #2

您不能完全使用此语法设计函数。试想一下:

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

auto value = get(key);

这是个谜语:

请告诉我返回类型和 .只能有一个答案,你只能用一种类型来回答,无论我问你这个问题多少时间,它都不会改变。getvalue

如果你不能回答,那么编译器也不能。

然而,这并不意味着你不能设计类似的东西来满足你的需求。

您可以只返回变体。这可能不完全是你要找的,但值得注意的是,因为它不会改变函数的任何输入。get

您也可以只发送预期的类型:

get<int>(key)

实现如下所示:

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

如果你不知道预期的类型,那么你可以只发送一个访客:

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(); 
    }
});

实现如下所示:

auto get(int key, auto visitor) -> decltype(auto) {
    return std::visit(visitor, m[key]);
}
2赞 Ted Lyngmo 1/27/2023 #3

如果我了解这些要求,您可以从中返回一个代理对象,该对象保留对 的引用,并且根据您对代理对象的操作,它可以适应。例:getvariant

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

它可以这样使用:

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';
}

评论

1赞 463035818_is_not_an_ai 1/27/2023
嗯......与返回 然后调用变体没有什么不同。不过,OP 似乎出于某种原因想要他们的运行时,这是一种实现它的方法。std::variantgetget
2赞 Ted Lyngmo 1/27/2023
@463035818_is_not_a_number 是的,我同意。代理有时很有用。我不是 100% 确定这是其中之一,但也许 OP 在其中找到了一些想法