尝试/捕捉参考分配

时间:2017-03-25 12:30:16

标签: c++ reference try-catch

我想从返回引用的方法中捕获异常,而不从后来使用引用的调用中捕获相同的异常。例如:

try {
    Something &o = myMap.at(myIndex);
    foo(o);
} catch(std::out_of_range &e) {
    // If this was thrown by myMap.at, we can handle it
    // If this was thrown by foo, we can't, and need to propagate it
}

所以我想做这样的事情:

Something &o;
try {
    o = myMap.at(myIndex);
} catch(std::out_of_range &e) {
    // Handle the error
}
foo(o);

但当然,那是无效的。我可以想办法将foo的异常包装在另一个异常中,然后将其展开到try之外,但这样做比较混乱。还有更好的方法吗?

可在此处找到MCVE:https://ideone.com/DJHxpO

4 个答案:

答案 0 :(得分:6)

您可以使用立即调用的lambda表达式:

Something &o = [&]() -> decltype(auto) {
    try {
        return myMap.at(myIndex);
    } catch(std::out_of_range &e) {
        // Handle the error
        // Return some other object for o to refer to.
    }
}();
foo(o);

答案 1 :(得分:1)

您可以改为使用指针:

Something *o; // initialize it with nullptr if necessary
try {
    o = &myMap.at(myIndex);
} catch(std::out_of_range &e) {
    // Handle the error
}
foo(*o);      // check whether it's nullptr before dereference if necessary

答案 2 :(得分:1)

一种适用于所有C ++版本的简单方法是

bool mapped = false;
try {
    Something &o = myMap.at(myIndex);
    mapped = true;
    foo(o);
} catch(std::out_of_range &e) {
     if (mapped) throw;
     //   if we get to here, the exception was thrown by myMap.at()
}

如果myMap.at()失败,这也避免了引用引用其他对象的需要。

答案 3 :(得分:0)

我认为这是boost::optional(std :: in c ++ 17)的一个很好的用例。

您并非真的想要生成或处理和例外,因为不在地图中的项目不是特殊情况。

我想我会这样表达:

int myIndex = 1;
foo(maybe_at(myMap, myIndex).value_or_eval([]()->Something& {
    // perform error code here
    return default_something;
}));

此处的完整代码示例:

#include <map>
#include <type_traits>
#include <boost/optional.hpp>

template<class Container>
struct container_traits
{
    using maybe_const_type = std::remove_reference_t<Container>;
    using container_type = std::decay_t<Container>;
    using is_const = std::is_const<maybe_const_type>;
    using key_type = typename container_type::key_type;
    using raw_mapped_type = typename container_type::mapped_type;
    using mapped_type = std::conditional_t<is_const::value, std::add_const_t<raw_mapped_type>, raw_mapped_type>;
    using mapped_reference = std::add_lvalue_reference_t<mapped_type >;
};

template<class Container, class Key>
auto maybe_at(Container&& container, Key&& key)
{
    using traits = container_traits<Container>;
    using result_type = boost::optional<typename traits::mapped_reference>;

    auto result = result_type {};
    auto ifind = container.find(key);
    if (ifind != container.end()) {
        result = ifind->second;
    }
    return result;
}


struct Something {};

void foo(Something&) {}
void foo(const Something&) {}

std::map<int, Something> myMap1;
const std::map<int, Something> myMap2;

auto default_something = Something{};

int main() {

    int myIndex = 1;

    foo(maybe_at(myMap1, myIndex).value_or_eval([]()->Something& {
        // perform error code here
        return default_something;
    }));

    foo(maybe_at(myMap2, myIndex).value_or_eval([]()->Something const& {
        // perform error code here
        return default_something;
    }));

}