C++ 我可以打开std::map迭代器到optionals的结构化绑定吗?

C++ 我可以打开std::map迭代器到optionals的结构化绑定吗?,c++,c++17,c++20,structured-bindings,C++,C++17,C++20,Structured Bindings,考虑以下代码: #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.e

考虑以下代码:

#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;
}
我认为不是,因为结构化绑定是语言级别的东西,std::optional是一个库特性,而且afaik没有办法自定义交互


注意:我假设我可以实现我自己的映射,它返回迭代器,知道迭代器是否指向.end,并攻击自定义点以执行可选逻辑。基于这一点,当我不控制容器时,我要求提供通用用例。

您可以添加一个类似

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};
}
然后你会像这样使用它

const auto&[_, str] = my_find(numberToStr, 2);
// _ is std::optional<int>, str is std::optional<str>
if (!str){
    return 1;
}
std::cout << *str;
auto str = my_find(numberToStr, 2);
// str is std::optional<str>
if (!str){
    return 1;
}
std::cout << *str;
如果您只关心值,那么只需使用

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};
}
然后你会像这样使用它

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

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

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};
}
然后你会像这样使用它

const auto&[_, str] = my_find(numberToStr, 2);
// _ is std::optional<int>, str is std::optional<str>
if (!str){
    return 1;
}
std::cout << *str;
auto str = my_find(numberToStr, 2);
// str is std::optional<str>
if (!str){
    return 1;
}
std::cout << *str;
如果您只关心值,那么只需使用

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};
}
然后你会像这样使用它

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

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;

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;

所需的API对我来说毫无意义。你为什么要拿回两张可选票?关键点要么在地图上,要么不在地图上,这是可选性的一个维度——这不像你可以取回一个已接合的关键点,而是一个未接合的值,或者一个未接合的关键点,而是一个已接合的值

空气污染指数应为:

样板 自动尝试查找地图&,键&->可选; 但是我们不能使用std::optional编写它,因为它不支持可选引用。返回一个实际的可选值既浪费了额外的副本,也可能在语义上无效—您可能想要那个特定的值,而不仅仅是一个值

因此,第一步是获得更好的optional实现并使用它。在这一点上,这里的实现非常简单:

样板 自动尝试\u findMap&m、键和键->可选 { autoit=m.findstd::forwardk; 如果是!=m.end{ 归还它; }否则{ 返回nullopt; } } 另一种使用std::optional的方法是返回可选迭代器,而不是可选引用。这样做的好处是,在仍然完全在标准库中工作的情况下,它与可选库一样具有可组合性

第三种方法是返回一个范围:

样板 自动尝试\u findMap&m,键常量&k->子范围 { 自动[f,l]=m.equal_rangekey; 返回子范围f,l; } 这仍然可以与所有范围的内容组合。您只需检查是否为空,而不是空:

自动r=尝试查找,键; 如果r为空{ //没有 }否则{ //使用右前 }
所需的API对我来说毫无意义。你为什么要拿回两张可选票?关键点要么在地图上,要么不在地图上,这是可选性的一个维度——这不像你可以取回一个已接合的关键点,而是一个未接合的值,或者一个未接合的关键点,而是一个已接合的值

空气污染指数应为:

样板 自动尝试查找地图&,键&->可选; 但是我们不能使用std::optional编写它,因为它不支持可选引用。返回一个实际的可选值既浪费了额外的副本,也可能在语义上无效—您可能想要那个特定的值,而不仅仅是一个值

因此,第一步是获得更好的optional实现并使用它。在这一点上,这里的实现非常简单:

样板 自动尝试\u findMap&m、键和键->可选 { autoit=m.findstd::forwardk; 如果是!=m.end{ 归还它; }否则{ 返回nullopt; } } 另一种使用std::optional的方法是返回可选迭代器,而不是可选引用。这样做的好处是,在仍然完全在标准库中工作的情况下,它与可选库一样具有可组合性

第三种方法是返回一个范围:

样板 自动尝试\u findMap&m,键常量&k->子范围 { 自动[f,l]=m.equal_rangekey; 返回子范围f,l; } 这仍然可以与所有范围的内容组合。您只需检查是否为空,而不是空:

自动r=尝试查找,键; 如果r为空{ //没有 }否则{ //使用右前 }
我不明白。如果你不控制容器,你就会得到你想要的。你期待什么样的魔法?如果对.find的调用被包装在一个返回一对可选的函数中是否可以?@cigien我碰到了一堵墙,如果我知道解决方案的方向,我会提供它,但我只知道我想要什么。find返回一对可选的-s,内森的回答是,我认为最好不要把WG21这样的东西烘焙成语言…当然,这很好。你的措辞让我有点反感,仅此而已。顺便说一句,这不是语言问题,而是库问题,因为它处理map::find返回的内容。因此,我认为这将取决于LWG。并不是说签名永远不会改变

做你想做的。这个问题对我来说没有意义。代码已经在检查find的结果,以确保返回的迭代器有效,在这种情况下,map元素的字符串也是有效的,不能是可选的。当您可以直接从迭代器访问字符串时,根本不需要使用结构化绑定,例如:if==numberToStr.end{return 1;}std::cout second@齐根:没问题:有一种方法可以告诉你这些事情,但你必须知道它的存在!我不明白。如果你不控制容器,你就会得到你想要的。你期待什么样的魔法?如果对.find的调用被包装在一个返回一对可选的函数中是否可以?@cigien我碰到了一堵墙,如果我知道解决方案的方向,我会提供它,但我只知道我想要什么。find返回一对可选的-s,内森的回答是,我认为最好不要把WG21这样的东西烘焙成语言…当然,这很好。你的措辞让我有点反感,仅此而已。顺便说一句,这不是语言问题,而是库问题,因为它处理map::find返回的内容。因此,我认为这将取决于LWG。并不是说签名会被更改成你想要的样子。这个问题对我来说没有意义。代码已经在检查find的结果,以确保返回的迭代器有效,在这种情况下,map元素的字符串也是有效的,不能是可选的。当您可以直接从迭代器访问字符串时,根本不需要使用结构化绑定,例如:if==numberToStr.end{return 1;}std::cout second@齐根:没问题:有一种方法可以告诉你这些事情,但你必须知道它的存在!是的,可能是OP想要的,很好的回答:我不应该等待评论的确认:这是OP要求的,但我认为std::optional my_atconst std::map&map,const Key&to会更好_find@Caleth说得好。在答案中添加了这一点。这样包装find有什么意义?@nosensetal optional是拼写T的一种冗长方式*是的,可能是OP想要的,很好的回答:我不应该等待评论的确认:这是OP要求的,但我想他们最好使用std::optional my_atconst std::map&map,常量键和键_find@Caleth说得好。在答案中添加了这一点。这样包装find有什么意义?@nosenseal optional是一种拼写T*的冗长方式。您能解释一下为什么检查空范围比检查迭代器是否指向末尾更好/更惯用吗?这不就是空范围吗?@Cedric它允许使用for循环编写清晰正确的代码,以保护对可能空范围的访问。谢谢你的参考@Cedric to me range的东西不太惯用,但可能只是为了适应它,我记得我第一次看到它时不喜欢boost::optional…你能解释一下为什么检查空范围比检查迭代器是否指向末尾更好/更惯用吗?这不就是空范围吗?@Cedric它允许使用for循环编写清晰正确的代码,以保护对可能空范围的访问。谢谢你的参考@Cedric to me range的东西不太地道,但也许这只是一种习惯,我记得第一次看到它时我不喜欢boost::optional…这是我个人的偏好,我更喜欢我可以检查just str并立即使用它,而不需要先检查一对是否合适,然后再从中获得第二个。我对其他a评论进行了长时间的讨论,这是我个人的偏好,很难强迫别人同意与迭代器的可选语法相同,我不喜欢**或等效语法。由于我不知道tooptionalStd::optional也是一个选项,所以仍然对答案投了赞成票。@ecatmur但这不是你想要做的,它只是一个针对库缺陷的破解。range\u reference\t不需要是实际的引用类型,也许我有一个生成代理的映射-我想保留它。另一个问题是它不是可选的好吧,那是可选的。遗憾的是,我们有展开参考,但没有反向操作。这是我个人的偏好,我希望我可以检查str并立即使用它,而无需首先检查配对是否正确,然后从中获得第二个。我对其他a评论进行了长时间的讨论,这是我个人的偏好,很难强迫别人同意与迭代器的可选语法相同,我不喜欢**或等效语法。由于我不知道tooptionalStd::optional也是一个选项,所以仍然对答案投了赞成票。@ecatmur但这不是你想要做的,它只是一个针对库缺陷的破解。range\u reference\t不需要是实际的引用类型,也许我有一个生成代理的映射-我想保留它。另一个问题是它不是可选的,那是可选的 母鸡很遗憾,我们有展开参考,但没有反向操作。