C++ try 中的变量范围

C++ scope of variables inside try

本文关键字:变量 范围 try C++      更新时间:2023-10-16

请考虑以下代码:

try {
    const Asdf &a = map1.at(index1);
    const Bsdf &b = map2.at(index2);
} catch(std::out_of_range&) {
    return false;
}
// <code>
std::cout<<a[b[42]]; // May throw std::out_of_range which should not be caught here.
return true;

<code>使用 a 和 b。我有两个选择:

  • <code>放入 try 块中
  • 在 try 块中获取指针,然后取消引用它们

第一个选项是错误的,因为如果<code>抛出std::out_of_range函数将返回 false,这应该仅在地图查找失败时发生。

第二个选项可能有点难看:

const Asdf *a;
const Bsdf *b;
try {
    a = &map1.at(index1); // What?
    b = &map2.at(index2);
} catch(std::out_of_range&) {
    return false;
}
std::cout << (*a)[(*b)[42]];
return true;

有没有更好的方法?像 Python 中的 try-except-else 这样的东西会很好,但这在C++中不存在。

没有必要进行任何异常处理。 std::map::find ,给定一个密钥,会给你一个迭代器。如果该元素在映射中不存在,则find将返回end迭代器(即 map.end() )。

取消引用迭代器时,您将收到一对值。第一个是键,第二个是对象。

auto aIt = map1.find(index1);
auto bIt = map2.find(index2);
if(aIt == map1.end() || bIt == map2.end())
{
    return false;
}
const Asdf &a = aIt->second;
const Bsdf &b = bIt->second;
std::cout << a[b[42]];
return true;

请注意,C++中的迭代器被定义为begin迭代器位于开头,而end迭代器超过最后一个元素 (http://en.cppreference.com/w/cpp/iterator/end),即容器内迭代器的范围为:[begin, end)。

解决方案 1:

为什么要将代码包含在 try catch 中,将其嵌入到自己的 try catch 块中以区分这两种情况?

try {
    const Asdf &a = map1.at(index1);
    const Bsdf &b = map2.at(index2);
    try {
        // <code>
        std::cout<<a[b[42]]; // May throw std::out_of_range which should not be caught here.
    } catch (std::out_of_range&) {}
} catch(std::out_of_range&) {
    return false;
}
return true;

但是,当然,在这种方法中,您不能将<code>中可能发生的out_of_range转发到函数的外部。

解决方案 2:

另一种方法是简单地使用 map::count() 检查键是否存在,而无需异常捕获:

if (map1.count(index1)==0 || map2.count(index2)==0) {
    return false; 
}
const Asdf &a = map1.at(index1);
const Bsdf &b = map2.at(index2);
// <code>
std::cout<<a[b[42]]; 
return true;

我最喜欢 Miguel 的解决方案,因为它不涉及异常处理(当不需要时)。

但除此之外,还有另一种选择(我喜欢简短并保持低map操作计数):

bool retval = false;
try{
    const Asdf &a=map1.at(index1);
    const Bsdf &b=map2.at(index2);
    retval = true;
    std::cout<<a[b[42]];
}catch(std::out_of_range&){
    return reval;
}
// more code?    
return reval;

一种非常规的解决方案是利用 lambda 的捕获将引用变量的范围扩展到块的范围之外。由于引用引用的对象在范围块之外有效,因此只要映射对象保留在范围内,捕获的引用在以后使用时就不会过时。

举个例子

#include <functional>
#include <vector>
int main()
{
    std::vector<std::vector< int > > map1 = { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
                                                { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
                                                { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
                                                { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
                                                { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } };
    std::function<int()> fn;
    try{
        const auto &a = map1.at(1);
        const auto &b = map1.at(2);
        fn = [&]() {return a[b[1]]; };
    }
    catch (std::out_of_range&){
        return false;
    }
    fn(); // Any exception thrown here would be caught separately from the above try catch block
} 

一种解决方法是确保地图确实包含该项目。它增加了开销,但与我知道的许多更糟糕的方式相比,它并不那么糟糕。

try{
    map1.at(index1);
    map2.at(index2);
}catch(std::out_of_range&){
    return false;
}
const Asdf &a=map1.at(index1);
const Bsdf &b=map2.at(index2);

或者如果以更好的方式编写(对不起,没有性能提升,只有可读性),除非你想牺牲引用的const性。

if(map1.find(index1) == map1.end() || map2.find(index2) == map2.end()) return false;
const Asdf &a=map1.at(index1);
const Bsdf &b=map2.at(index2);

你也可以使用 std::map::const_iterator 而不需要 try-catch 块。

std::map::const_iterator a = map1.find(index1);
if(a == map1.end()) return false;
std::map::const_iterator b = map1.find(index2);
if(b == map2.end()) return false;

使用只读a->secondb->second执行任何操作。