C++ 具有强异常保证的STL容器同时插入

C++ 具有强异常保证的STL容器同时插入,c++,c++11,exception,stl,invariants,C++,C++11,Exception,Stl,Invariants,我想同时插入几个STL容器,要么全部成功,要么全部保持不变。我怎样才能优雅地做到这一点 在我的示例中,我有一个自定义容器,其中包含各种STL容器作为数据成员。插入操作将插入到各种容器中,但如果其中一个容器失败,则需要撤消更改以确保所有不变量仍然有效。我怎样才能(很好地)做到这一点 我的第一个直觉是回顾所有插入并撤销它们。这似乎有些过分,但我希望我的例子能说明这一点 示例(如果不够“最低限度”,则表示抱歉): struct数据项{ int x; 浮动y; 字符c; }; 类霉菌容器{ 公众: 无效

我想同时插入几个STL容器,要么全部成功,要么全部保持不变。我怎样才能优雅地做到这一点

在我的示例中,我有一个自定义容器,其中包含各种STL容器作为数据成员。插入操作将插入到各种容器中,但如果其中一个容器失败,则需要撤消更改以确保所有不变量仍然有效。我怎样才能(很好地)做到这一点

我的第一个直觉是回顾所有插入并撤销它们。这似乎有些过分,但我希望我的例子能说明这一点

示例(如果不够“最低限度”,则表示抱歉):

struct数据项{
int x;
浮动y;
字符c;
};
类霉菌容器{
公众:
无效插入(标准::向量名称,
std::无序集合类别,
数据项数据);
私人:
/*不变量:如果字符串“name”映射到“name\u to\u id\u”中的id“i”
它必须是“数据”中“数据项”对象的位置
并且包含在至少一个类别中*/
std::将名称映射到id;
地图分类;
std::矢量数据;
};
MyContainer::Insert(标准::向量名称,
std::无序集合类别,
数据项(数据){
/*确保名称和类别不为空且数据有效*/
int id=数据大小();
for(auto it=names.begin();it!=names.end();+it){
如果(此->名称到id计数(名称)){
/*通过向后迭代到names.begin()来清除迄今为止插入的所有名称
使用此->名称\u来\u标识\u。擦除*/
抛出一些异常(“插入失败”);
}
试一试{
这个->名称到id插入({*it,id});
}捕获(/*我在这里捕获什么?*/){
/*通过向后迭代到names.begin()来清除迄今为止插入的所有名称
使用此->名称\u来\u标识\u。擦除*/
抛出一些异常(“插入失败”);
}
}
对于(自动it=类别。开始();it!=类别。结束();+it){
试一试{
此->分类在(*it).insert(id);
}捕获(/*我在这里捕获什么?*/){
/*清理迄今为止插入的所有名称和所有类别id
使用向后迭代到categories.begin()
此->分类在(*it).erase(id)*/
抛出一些异常(“插入失败”);
}
}
试一试{
此->数据。推回(数据);
}捕获(/*我在这里捕获什么?*/){
/*再次清理所有类别和名称*/
抛出一些异常(“插入失败”);
}
}
即使没有写下清理工作,这似乎也太过分了。特别是考虑到
insert
push_back
应该很少失败。这真的是我需要做的吗

另外,我能确定的最安全的擦除对
std::unordered_集
std::map
所做更改的方法是将
find
erase
结合起来,但我在
find
的异常安全性上找不到任何东西。它总是成功吗


我突然想到,
insert
push_back
语句必须包装在
try
块中才能真正处理异常,但是我捕获什么异常呢?

如果可以修改
DataItem
的构造函数,您可以让它从上一项的构造函数将内容插入到下一个容器中。这里有一个工作演示

  #include <vector>
  #include <list>
  #include <functional>
  #include <iostream>
  
  struct mydata
  {   
      int a;
      double b;
      template <typename F>                   
      mydata (int a, double b, F func) : a(a), b(b) { func(); }
  };                                                           
    
  int main()
  {         
      std::vector<mydata> one;
      std::vector<mydata> two;
      std::list<mydata> three;
      std::list<mydata> four; 
                             
      try {               
          one.emplace_back  (1, 2, [&]() {
          two.emplace_back  (1, 2, [&]() { 
          three.emplace_back(1, 2, [&]() { 
          four.emplace_back (1, 2, [ ]() { 
          throw 42; // simulate failure at the last one
          }); }); }); });
      } catch (int x)  {                                          
          // handle the failure
      }                 
       
      std::cout << one.size() << " " 
                << two.size() << " " 
                << three.size() << " " 
                << four.size() << "\n";
  }
#包括
#包括
#包括
#包括
结构mydata
{   
INTA;
双b;
模板
mydata(inta,double b,F func):a(a),b(b){func();}
};                                                           
int main()
{         
std::向量1;
std::向量二;
std::列表三;
std::列表四;
试试{
一、后置(1、2、[&](){
二、后置(1,2,[&](){
三、后置(1、2、[&](){
四、后置(1、2、[…(){
抛出42;//模拟最后一次失败
}); }); }); });
}捕获(int x){
//处理失败
}                 

std::cout如果您可以修改
DataItem
的构造函数,您可以让它从上一个项的构造函数中插入内容到下一个容器中

  #include <vector>
  #include <list>
  #include <functional>
  #include <iostream>
  
  struct mydata
  {   
      int a;
      double b;
      template <typename F>                   
      mydata (int a, double b, F func) : a(a), b(b) { func(); }
  };                                                           
    
  int main()
  {         
      std::vector<mydata> one;
      std::vector<mydata> two;
      std::list<mydata> three;
      std::list<mydata> four; 
                             
      try {               
          one.emplace_back  (1, 2, [&]() {
          two.emplace_back  (1, 2, [&]() { 
          three.emplace_back(1, 2, [&]() { 
          four.emplace_back (1, 2, [ ]() { 
          throw 42; // simulate failure at the last one
          }); }); }); });
      } catch (int x)  {                                          
          // handle the failure
      }                 
       
      std::cout << one.size() << " " 
                << two.size() << " " 
                << three.size() << " " 
                << four.size() << "\n";
  }
#包括
#包括
#包括
#包括
结构mydata
{   
INTA;
双b;
模板
mydata(inta,double b,F func):a(a),b(b){func();}
};                                                           
int main()
{         
std::向量1;
std::向量二;
std::列表三;
std::列表四;
试试{
一、后置(1、2、[&](){
二、后置(1,2,[&](){
三、后置(1、2、[&](){
四、后置(1、2、[…(){
抛出42;//模拟最后一次失败
}); }); }); });
}捕获(int x){
//处理失败
}                 

std::cout我没有说明实际的清理操作,也没有说明最有效的方法是什么。您需要记录插入失败的阶段,并相应地继续

struct DataItem {
   int x;
   float y;
   char c;
};

class SomeException : std::exception
{
public:
   SomeException(char *c) : std::exception(c) {};

};

class MyContainer {
public:
   void Insert(std::vector<std::string> names,
      std::unordered_set<int> categories,
      DataItem data);

private:
   void CleanUp();

private:
   /* invariant: if a string 'name' is mapped to an id 'i' in 'name_to_id_'
   it must be the position of a 'DataItem' object in 'data_'
   and contained in at least one of the categories */
   std::map<std::string, int> name_to_id_;
   std::map<int, std::unordered_set<int>> categories_;
   std::vector<DataItem> data_;
};


void MyContainer::CleanUp()
{
   // Do your clean up here
}

void MyContainer::Insert(std::vector<std::string> names,
   std::unordered_set<int> categories,
   DataItem data) 
{
   bool failed = false;

   /* ensure names, and categories are not empty and data is valid */
   int id = data_.size();
   for (auto it = names.begin(); it != names.end() && !failed; ++it) {
      if (this->name_to_id_.count(*it))
         failed = true;
      try {
         this->name_to_id_.insert({ *it, id });
      }
      catch (...) {
         failed = true;
      }
   }

   for (auto it = categories.begin(); it != categories.end() && !failed; ++it) {
      try {
         this->categories_.at(*it).insert(id);
      }
      catch (...) {
         failed = true;
      }
   }

   try {
      if (!failed)
         this->data_.push_back(data);
   }
   catch (...) {
      failed = true;
   }

   if (failed)
      CleanUp();
}
struct数据项{
int x;
浮动y;
字符c;
};
类SomeException:std::exception
{
公众:
SomeException(char*c):std::exception(c){};
};
类霉菌容器{
公众:
无效插入(标准::向量名称,
std::无序集合类别,
数据项数据);
私人:
空洞清理();
私人:
/*不变量:如果字符串“name”映射到“name\u to\u id\u”中的id“i”
它一定是一个