【问题标题】:C++ scope of variables inside trytry 中变量的 C++ 范围
【发布时间】:2015-04-19 12:50:43
【问题描述】:

考虑这段代码:

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;

&lt;code&gt; 使用 a 和 b。我有两个选择:

  • &lt;code&gt; 放入try 块中
  • 在 try 块中获取指针,然后取消引用它们

第一个选项是错误的,因为如果 &lt;code&gt; 抛出 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++ 中不存在。

【问题讨论】:

  • 如果&lt;code&gt; 抛出std::out_of_range 那么你应该抓住它,有什么问题?
  • @maroun-maroun 我不想从&lt;code&gt;抓到std::out_of_range
  • 然后用内部try子句包围它。
  • 不确定它是否比指针更好,但是std::reference_wrapper&lt;const Foo&gt;
  • @johannes-schaub-litb std::reference_wrapper&lt;Foo&gt; 不会从 Foo 继承 operator[]。这使得它比第二个选项更糟糕。

标签: c++ try-catch stdmap


【解决方案1】:

没有必要进行任何异常处理。 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 , 结束)。

【讨论】:

    【解决方案2】:

    解决方案 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;
    

    但当然,在这种方法中,您不能将 out_of_range 转发到您的函数外部,而 out_of_range 会出现在您的 &lt;code&gt; 中。

    解决方案 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;
    

    【讨论】:

    • 我想让&lt;code&gt;抛出的std::out_of_range离开函数。
    • @BaummitAugen 但在这种情况下,它将被外部块再次捕获。我将编辑另一个提案。
    • @HristoVenev 我添加了另一个提案来解决这个要求。
    • 与采用迭代器的解决方案相比,选项 2 似乎很浪费,因为它会为每个键搜索映射两次,除非编译器能够对其进行优化
    【解决方案3】:

    我最喜欢Miguel's solution,因为它不涉及异常处理(当不需要时)。

    但除此之外,还有另一种选择(我喜欢它的简短并保持低 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;
    

    【讨论】:

      【解决方案4】:

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

      举个例子

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

      【讨论】:

        【解决方案5】:

        一种解决方法是确保地图确实包含该项目。它增加了开销,但在我所知道的许多更糟糕的方式中没有那么糟糕。

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

        或者如果以更好的方式编写(抱歉没有性能提升,只有可读性),除非您想牺牲引用的constness。

        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-&gt;secondb-&gt;second 做任何事情。

        【讨论】:

        • 这有点慢。不是我会做的事。它也与第二个选项一样长。我可以在 try-catch 之后将指针转换为引用。
        猜你喜欢
        • 1970-01-01
        • 2015-12-05
        • 1970-01-01
        • 1970-01-01
        • 2021-11-14
        • 2011-12-23
        • 2011-02-20
        • 2017-10-30
        • 1970-01-01
        相关资源
        最近更新 更多