STL: Инициализация контейнера с помощью неконструктивного компаратора с состоянием

Я думал об этом как о возможном решении проблемы, однако это довольно очевидное техническое нарушение что-то в 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, которыми он управляет, пусты или будут очищены () вскоре после этого.

2

Решение

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.

Это:

  1. создает m_sorter первый так что вы ничего не делаете с неинициализированным объектом
  2. проходит это по ссылке к SorterFunc объект
  3. использует копию этого 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;
};
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]