尽管在右侧有例外,但C++中的赋值仍然发生

jma*_*jma 72 c++ c++14

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

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

这有效.有时GetStuff()抛出一个异常,这很好,因为如果它,我不想在垃圾地图中的值.

但起初我在循环中写了这个,这不起作用:

junk[id] = GetStuff();
Run Code Online (Sandbox Code Playgroud)

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

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

我在这里误解了C++的原理吗?

Som*_*ude 95

在C++ 17之前,赋值运算符的左侧和右侧之间没有排序.

它首先在C++ 17中引入了显式排序(首先评估右侧).

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

有关详细信息,请参阅此评估订单参考(尤其是第20点).

  • 如果我正确理解了引用,则可以通过删除"overloaded"来缩短答案中的第一个句子,因为在C++ 17中首先评估右侧的重载和非重载赋值运算符. (4认同)

Ted*_*gmo 17

的std ::地图::操作符[]

返回对映射到等效于key的键的值的引用,如果此类键尚不存在则执行插入.

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


pad*_*ddy 12

你误解了如何operator[]运作std::map.

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

为了使这项工作符合您的预期,您需要使用std::map::insert(*):

junk.insert(std::make_pair(id, GetStuff()));
Run Code Online (Sandbox Code Playgroud)

警告:insert如果id尚未映射,则仅添加值.

  • @ user463035818:提问者表达了一个信任,即发生了一个赋值,强烈暗示他们不知道评估`operator []`会为自己的`junk [id]`创建一个空集. (8认同)
  • 我不认为对于'operator []`如何在这里工作有任何误解.同样从你的回答中不清楚为什么`operator =`评估左侧,即使右侧抛出异常 (6认同)