STL: Initializing a Container with an Unconfigured State Comparator

This is thought to be a possible solution to the problem, however, since this is a fairly obvious technical violation of something in C ++, I wanted to know how likely it is that it will happen, is there another fairly obvious approach, etc. I hope this doesn't get caught up in the flamewar about undefined behavior, but given the topic I'm a bit anticipating.

This is not the code that I am writing, I hope it is not too simplistic to describe what I am trying to do.

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;
};

      

Target here

I have often found that when writing template classes that have a base class without templates with as much factoring logic as possible, it is useful to use both common codes of the other class, which can be referenced and reduce the amount of code duplication, especially when creating five different versions of the same class with different numbers of template parameters.

In this case, CodePtr is generated elsewhere in the code (although I wrote it), and I would like to find elements based on an arbitrary number of arbitrary data types. At first I considered std :: multimap, but the key eventually became a wrapper (or a copy of a significant chunk) of the CodePtr again.

Problem

I pass the constructor of the state sorter Sort SortObject5 <> :: my_sorter to the constructor SortedBase :: m_codeList. However, due to the fact that the state sorter that is in the subclass is not explicitly built at the point where the STL set is built.

I am wondering if this is a problem if I am not doing any insertions or searches into m_codeList from any constructor.

Adversarial Disclaimer

I formally ASSERT () that the rules used by any sorting functor with status will only change when either the STL containers it controls are empty or are cleared () shortly thereafter.

+2


source to share


1 answer


An object std::set<CodePtr,Sorter>

stores an instance Sorter

by value , so when you instantiate it with Sorter*

(did you mean this is a reference not a pointer?) It will slice the object and keep the main body.

This means that the copy constructor Sorter

will run and make a copy of the uninitialized object. Undefined.

Assuming you can even instantiate Sorter

if it's an abstract type, which you can't (I don't know what yours does PURE

, but I assume you are making the function pure virtual)

@ Superimposed comment suggests a good approach, base from the member idiom will allow you to ensure that the object is initialized first m_sorter

, which is part of the problem. However, this will not solve the problem, to decide that you need a wrapper around the sorter, for example.

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

      

And then pass the wrapper to the set constructor:

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

      

If you build std::function

from a derived type, it will not be sliced. It will be copied, but you can prevent this by using a referenced wrapper:

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

      



It's m_sorter

already initialized here because it is stored in the base class BaseFromMember

using the base-from-member idiom.

It:

  • creates the m_sorter

    first one , so you don't do anything with an uninitialized object
  • passes object 2> by referenceSorterFunc

  • uses a copy of this SorterFunc

    (still containing a reference to m_sorter

    ) as a comparison function forstd::set

If you don't want to use the base-from-member idiom, then it's still easy to avoid the Undefined behavior of your source code, just create by default set

(instead of passing it an uninitialized object) then assign a new value to it before you start filling it:

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

      

No new base classes, no extra templates, no Undefined behavior.

Here's the complete working code:

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;
};

      

0


source







All Articles