尝试/捕获参考分配

try/catch around reference assignment

本文关键字:参考 分配 尝试      更新时间:2023-10-16

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

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

您可以使用立即调用的 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);

您可以改用指针:

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

我认为这是boost::optional的一个很好的用例(std::在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;
    }));
}

适用于所有版本的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()的需要。