使用可能抛出的表达式初始化 const 变量的推荐方法

Recommended way to initialize a const variable with an expression which might throw

你可能知道这样的情况,你只想用一个可能会失败(抛出)(例如container.at())的表达式分配给一个(const)变量,这迫使你编写锅炉车牌代码:

void foo(const string &key) {
    auto it = data_store.find(key);
    if (it == data_store.end()) {
        return;
    }
    const auto & element = it->second;
    ...
    go on with `element`...
    ...
}

在 Python 中你可以这样写代码:

def foo(name):
    try:
        element = data_store[key]
    except KeyError:
        return
    ..
    go on with `element`
    ..

.. with 噪音较小,因为您不会为了检查是否存在而引入无用的额外 it

如果 C++ 的 try 不会引入变量范围,您可以使用 at():

void foo(const string &key) {
    try {
        const auto & element = data_store.at(key);
    } catch (const out_of_range &) {
        return;
    }
    ...
    go on with `element`...
    ...
}

如果你不想放弃 constness 并保持你的代码干净,有什么方法可以去这里?

如果 lambda 只能有一个 try/catch 主体,你可以写

void foo(const string &key) {
    const auto & element = [&] () -> T try {
        return data_store.at(key);
    } catch () {
        return;
    } ();
    ...
    go on with `element`...
    ...
}

一些类似问题的答案建议 try/catch 块围绕所有代码:

void foo(const string &key) {
    try {
        const auto & element = data_store.at(key);
        ...
        go on with `element`...
        ...
    } catch (const out_of_range &) {
        return;
    } catch (some other exception) {
        ...
    } catch (some other exception) {
        ...
    }
}

但我不喜欢这个,原因有以下三个:

您知道哪些(漂亮、简短、干净)替代品?

this thread 上有三个不错的选择,没有其他选择。

这些情况假设我们正在初始化一个对象;要按原样初始化引用,请将这些技术应用于 std::reference_wrapper 或指针。


顺便说一句,我不会这么快打折您的第一个代码示例。它比所有其他选项都简单,而且 C++ 中的一个常见建议是仅在异常情况下使用异常——您不希望它们成为函数契约的正常部分。将它们用作快捷方式不是惯用的。

换句话说,如果函数设计是在查找失败时什么也不做,那么抛出捕获对函数来说是一个不必要的复杂化。您刚刚编写了一个更丑陋的 C 风格错误处理版本。

at() 访问器的全部意义在于,您的函数可以通过 而不是 捕获来保持简单——异常可以被传播到更多通用错误处理程序。