STL:使用未构造的状态比较器初始化容器

时间:2013-05-17 15:47:18

标签: c++ stl construction

这一直是我脑海中的一个问题的可能解决方案,但是因为它是一个相当明显的技术违反C ++中的某些,我想知道失败的可能性有多大,是否还有另一种相当明显的方法,等等。我希望这不会引发关于未定义行为的火焰,但考虑到这个话题,我确实期待一点点。

这不是我正在编写的代码,我希望它不会太简单,不能描述我想要做的事情。

class Code
{
public:
  bool read(short slot, short& val);
  bool read(short slot, long& val);
  bool read(short slot, double& val);
  // etc
protected:
  unsigned char* m_data;
};
typedef boost::shared_ptr<Code> CodePtr;

class SortedBase
{
protected:
   class Sorter : public std::binary_function<CodePtr,CodePtr,bool>
   {
   protected:
     inline Sorter() {}
     virtual ~Sorter() {}
   public:
     virtual bool operator()(CodePtr left, CodePtr right) PURE;
   };

   inline SortedBase(Sorter* s):m_codeList(s) {}

   typedef std::set<CodePtr,Sorter> TSortedCode;
   TSortedCode m_codeList;
public:
   virtual ~SortedBase() {}
   void fetch(); // populates m_codeList
};

template<class SORT1, class SORT2, class SORT3, class SORT4, class SORT5>
class SortedObject5 : public SortedBase
{
public:
  SortedObject5():SortedBase(m_sorter),m_sorter(this) {}

  something_interesting find(SORT1 val1, SORT2 val2, SORT3 val3, SORT4 val4, SORT5 val5);
protected:
  typedef SortedObject5<SORT1,SORT2,SORT3,SORT4,SORT5> my_class;
  class MySorter : public Sorter
  {
  public:
    MySorter(const my_class& parent):m_parent(parent) {}
    virtual operator()(CodePtr left, CodePtr right);
  protected:
    const my_class& m_parent;
  }

  MySorter m_sorter;
};

意图

我经常发现,在编写具有非模板基类的模板类时,尽可能多的因素逻辑对于两者都有一些共同的类有用,其他代码可以引用并减少代码重复的数量,尤其是当使用不同数量的模板参数制作同一类的五个不同版本时。

在这种情况下CodePtr是在代码的其他地方生成的(尽管我写了它),我想找到基于任意数量的任意数据类型的元素。我首先考虑了一个std :: multimap,但是密钥最终会成为CodePtr的一个包装器(或者是一大块的副本)。

问题

我将有状态分类器仿函数SortedObject5&lt;&gt; :: my_sorter传递给SortedBase :: m_codeList的构造函数。但是因为有状态的分类器在子级中,显然不是在构造STL集的时候构造的。

如果我不在任何构造函数的m_codeList中进行任何插入或搜索,我想知道这是否是一个问题。

有状态分拣机免责声明

我正式ASSERT(),任何有状态排序仿函数使用的规则只有在它控制的STL容器为空或者不久之后才会清除()后才会改变。

1 个答案:

答案 0 :(得分:0)

std::set<CodePtr,Sorter>对象按价值存储Sorter 的实例,所以当你用Sorter*构建它时(你的意思是不是引用而不是指针?)它将切割对象并仅保留基本部分。

这意味着Sorter复制构造函数将运行并复制未初始化的对象。随之而来的是未定义的行为。

假设你甚至可以创建一个Sorter的实例,如果它是一个抽象类型你将无法(我不知道你的PURE做了什么,但我认为你是使函数纯虚拟)

@Angew的评论提出了一个很好的方法,成员习语的基础将允许你确保首先初始化m_sorter对象,这是问题的一部分。这对切片问题没有帮助,为了解决你需要在分拣机周围使用一些包装器,例如。

typedef std::function<bool(const CodePtr&,const CodePtr&)> SorterFunc;
typedef std::set<CodePtr, SorterFunc> TSortedCode;

然后将包装器传递给set构造函数:

inline SortedBase(SorterFunc s) : m_codeList(s) {}

如果从派生类型构造std::function,则不会对其进行切片。它将被复制,但您可以通过使用引用包装器来阻止它:

  SortedObject5() : BaseFrommember(this), SortedBase(SorterFunc(std::ref(m_sorter))) { }

m_sorter已经初始化,因为它存储在BaseFromMember基类中,使用成员基础成语。

此:

  1. 首先创建m_sorter ,以便您不对未初始化的对象执行任何操作
  2. 通过引用传递给SorterFunc对象
  3. 使用SorterFunc的副本(仍然保留对m_sorter的引用)作为std::set的比较函数
  4. 如果你不想使用base-from-member习语,那么仍然很容易避免原始代码的未定义行为,只需构造set(而不是传递未初始化的对象)然后在开始填充之前为其分配一个新值:

    SortedObject5() : m_sorter(this)
    {
      this->m_codeList = TSortedCode(SorterFunc(boost::ref(m_sorter)));
    }
    

    没有新的基类,没有额外的模板,没有未定义的行为。

    以下是完整的工作代码:

    class SortedBase
    {
    protected:
       class Sorter : public std::binary_function<CodePtr,CodePtr,bool>
       {
       protected:
         Sorter() {}
         virtual ~Sorter() {}
       public:
         virtual bool operator()(const CodePtr& left, const CodePtr& right) = 0;
       };
    
       typedef boost::function<bool(const CodePtr&, const CodePtr&)> SorterFunc;
    
       typedef std::set<CodePtr,SorterFunc> TSortedCode;
    
       TSortedCode m_codeList;
    
    public:
       virtual ~SortedBase() {}
       void fetch(); // populates m_codeList
    };
    
    template<class SORT1, class SORT2, class SORT3, class SORT4, class SORT5>
    class SortedObject5 : public SortedBase
    {
    public:
      SortedObject5() : m_sorter(*this)
      {
        this->m_codeList = TSortedCode(SorterFunc(boost::ref(m_sorter)));
      }
    
    protected:
      typedef SortedObject5<SORT1,SORT2,SORT3,SORT4,SORT5> my_class;
    
      class MySorter : public Sorter
      {
      public:
        MySorter(const my_class& parent):m_parent(parent) {}
        virtual bool operator()(const CodePtr& left, const CodePtr& right);
      protected:
        const my_class& m_parent;
      };
    
      MySorter m_sorter;
    };