我可以将 std::map 迭代器解包到可选的结构化绑定吗?

NoS*_*tAl 1 c++ c++17 structured-bindings c++20

考虑以下代码:

#include<functional>
#include<iostream>
#include<map>

const std::map<int, std::string> numberToStr{{1, "one"}, {2,"two"}};
int main() {
    auto it = numberToStr.find(2);
    if (it ==numberToStr.end()){
        return 1;
    }
    const auto&[_, str] = *it;
    std::cout << str;
}
Run Code Online (Sandbox Code Playgroud)

有什么方法可以让我解开it对 2 个可选项(_ 和 str)的潜在解引用,然后我可以写:

const auto&[_, str] = // some magic;
// _ is std::optional<int>, str is std::optional<str>
if (!str){
    return 1;
}
std::cout << *str;
}
Run Code Online (Sandbox Code Playgroud)

我认为不是,因为结构化绑定是语言级别的东西,并且 std::optional 是一个库功能,并且 afaik 无法自定义交互。

注意:我想我可以实现我自己的映射,它返回知道它们是否指向 .end() 的迭代器,并“hack”自定义点以基于此执行可选逻辑,当我不控制时,我要求使用一般用例容器。

Nat*_*ica 5

您可以添加一个辅助函数,如

template <typename Key, typename Value, typename... Rest>
std::pair<std::optional<Key>, std::optional<Value>> my_find(const std::map<Key, Value, Rest...>& map, const Key& to_find)
{
    auto it = map.find(to_find);
    if (it == map.end())
        return {};
    else
        return {it->first, it->second};
}
Run Code Online (Sandbox Code Playgroud)

然后你会像这样使用它

const auto&[_, str] = my_find(numberToStr, 2);
// _ is std::optional<int>, str is std::optional<str>
if (!str){
    return 1;
}
std::cout << *str;
Run Code Online (Sandbox Code Playgroud)

如果你只关心这个值,你可以通过返回它来稍微缩短代码

template <typename Key, typename Value, typename... Rest>
std::optional<Value> my_find(const std::map<Key, Value, Rest...>& map, const Key& to_find)
{
    auto it = map.find(to_find);
    if (it == map.end())
        return {};
    else
        return {it->second};
}
Run Code Online (Sandbox Code Playgroud)

然后你会像这样使用它

auto str = my_find(numberToStr, 2);
// str is std::optional<str>
if (!str){
    return 1;
}
std::cout << *str;
Run Code Online (Sandbox Code Playgroud)


eca*_*mur 5

更符合 C++20 习惯的路线是将迭代器建模为可能为空的范围:

auto const rng = std::apply(
    [](auto it, auto end) { return std::ranges::subrange(it, end); },
    numberToStr.equal_range(2));
if (rng.empty())
    return 1;
auto const& [_, str] = *rng.begin();
std::cout << str;
Run Code Online (Sandbox Code Playgroud)

例子

您可以在 C++20 之前使用 Boost.Ranges 来完成此操作,它具有更符合人体工程学的效果iterator_range

auto const rng = boost::make_iterator_range(numberToStr.equal_range(2));
// ditto
Run Code Online (Sandbox Code Playgroud)