Я думал об этом как о возможном решении проблемы, однако это довольно очевидное техническое нарушение что-то в 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<> :: my_sorter в конструктор SortedBase :: m_codeList. Однако, поскольку сортировщик с сохранением состояния находится в подклассе, он, очевидно, не создается в тот момент, когда создается набор STL.
Мне интересно, если это проблема, если я не делаю вставки или поиск в m_codeList из любого конструктора.
Заявление об отказе от ответственности
Я официально ASSERT (), что правила, используемые любым функтором сортировки с сохранением состояния, будут меняться только тогда, когда контейнеры STL, которыми он управляет, пусты или будут очищены () вскоре после этого.
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
базовый класс, используя идиому base-from-member.
Это:
m_sorter
первый так что вы ничего не делаете с неинициализированным объектомSorterFunc
объектSorterFunc
(все еще держит ссылку на m_sorter
) в качестве функции сравнения для std::set
Если вы не хотите использовать идиому 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;
};
Других решений пока нет …