【问题标题】:Assignment in C++ occurs despite exception on the right side尽管右侧出现异常,但 C++ 中的赋值仍然存在
【发布时间】:2019-04-08 20:08:27
【问题描述】:

我有一些 (C++14) 代码如下所示:

map<int, set<string>> junk;
for (int id : GenerateIds()) {
    try {
        set<string> stuff = GetStuff();
        junk[id] = stuff;
    } catch (const StuffException& e) {
        ...
    }
}

这行得通。有时GetStuff() 会抛出一个异常,这很好,因为如果是这样,那么我不想要垃圾映射中的值。

但一开始我是在循环中写的,这不起作用:

junk[id] = GetStuff();

更准确地说,即使GetStuff() 抛出异常,junk[id] 也会被创建(并分配一个空集)。

这不是我所期望的:我希望它们以相同的方式运行。

这里有没有我误解的C++原理?

【问题讨论】:

标签: c++ c++14


【解决方案1】:

在 C++17 之前,赋值运算符的左右两侧没有顺序。

在 C++17 中首次引入了显式排序(首先计算右侧)。

这意味着评估顺序是未指定,这意味着由实现来按照它想要的顺序执行评估,在这种情况下,它首先评估左侧。

请参阅this evaluation order reference 了解更多详情(尤其是第 20 点)。

【讨论】:

  • 如果我正确理解了引用,答案中的第一句可以通过删除“重载”来缩短,因为在 C+ 中首先评估重载和非重载赋值运算符的右侧+17。
【解决方案2】:

std::map::operator[]

返回对映射到等效键的值的引用 键,如果这样的键不存在,则执行插入。

junk[id] 导致上述插入,之后已经发生GetStuff() 抛出。请注意,在 C++14 中,这些事情发生的顺序是实现定义的,因此使用不同的编译器,如果 GetStuff() 抛出,您的 junk[id] = GetStuff(); 可能不会执行插入操作。

【讨论】:

    【解决方案3】:

    您误解了operator[]std::map 上的工作方式。

    它返回对映射项的引用。因此,您的代码首先在该位置插入一个默认项,然后调用operator= 来设置一个新值。

    要使这项工作按您期望的方式进行,您需要使用std::map::insert (*):

    junk.insert(std::make_pair(id, GetStuff()));
    

    警告insert 只会在 id 尚未映射时添加值。

    【讨论】:

    • 我不认为这里涉及operator[] 的工作方式有任何误解。同样从您的回答中不清楚为什么operator= 评估左侧,即使右侧抛出异常
    • 如果有什么误会,对我来说就是对a[b]=...的误会;不只是为了 [...]。
    • @user463035818:发问者表示相信发生了分配,这强烈表明他们不知道评估operator[]会自行为junk[id]创建一个空集。
    • 赞成建议一种无需首先默认构建条目的编写方式,无论是否存在“误解”。
    猜你喜欢
    • 1970-01-01
    • 2014-01-08
    • 2014-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多