The std::set<CodePtr,Sorter>
object stores an instance of Sorter
by value so when you construct it with a Sorter*
(did you mean that to be a reference not a pointer?) it will slice the object and only keep the base part.
That means the Sorter
copy constructor will run and make a copy of an uninitialized object. Undefined behaviour ensues.
That's assuming you can even create an instance of Sorter
, if it's an abstract type you won't be able to (I don't know what your PURE
does but I assume you're making the function pure virtual)
@Angew's comment suggest a good approach, the base from member idiom will allow you to ensure the m_sorter
object is initialized first, which is part of the problem. That doesn't help the issue of slicing though, to solve that you'd need some wrapper around the sorter e.g.
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 construct the std::function
from the derived type it won't be sliced. It will be copied though, but you can prevent that by using a reference wrapper:
SortedObject5() : BaseFrommember(this), SortedBase(SorterFunc(std::ref(m_sorter))) { }
Where m_sorter
is already initialized, because it is stored in the BaseFromMember
base class, using the base-from-member idiom.
This:
- creates the
m_sorter
first so you don't do anything with an uninitialized object - passes it by reference to a
SorterFunc
object - uses a copy of that
SorterFunc
(still holding a reference tom_sorter
) as the comparision function for thestd::set
If you don't want to use the base-from-member idiom then it's still easy to avoid the undefined behaviour of your original code, just default construct the set
(instead of passing it an uninitialized object) then assign a new value to it before you start populating it:
SortedObject5() : m_sorter(this)
{
this->m_codeList = TSortedCode(SorterFunc(boost::ref(m_sorter)));
}
No new base classes, no extra templates, no undefined behaviour.
Here's the working code in full:
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;
};