STL:用未构造的有状态比较器初始化容器
STL: Initializing a container with an unconstructed stateful comparator
这一直在我的脑海中运行,作为一个问题的可能解决方案,然而,由于这是一个相当明显的技术违反的东西在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,但是key最终会再次成为CodePtr的包装器(或重要块的副本)。
问题我将有状态排序函数SortedObject5<>::my_sorter传递给SortedBase::m_codeList的构造函数。但是,由于状态排序器位于子类中,因此很明显不是在构造STL集时构造的。
我想知道这是否是一个问题,如果我不做任何插入或搜索m_codeList从任何构造函数。
有状态排序器免责声明
我正式断言(),任何有状态排序函数所使用的规则只有在它控制的STL容器为空或不久之后被清除时才会改变。
std::set<CodePtr,Sorter>
对象通过值存储Sorter
的实例,因此当您使用Sorter*
构建它时(您是指作为引用而不是指针吗?)它将切片对象并仅保留基部分。
这意味着Sorter
复制构造函数将运行并复制未初始化的对象。未定义的行为随之而来。
这是假设你甚至可以创建Sorter
的实例,如果它是一个抽象类型,你将无法(我不知道你的PURE
做什么,但我假设你使函数纯虚)
@Angew的评论建议了一个很好的方法,base from成员习惯用法将允许您确保首先初始化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;
};
- std::设置自定义比较器
- C++中"std::sort"比较器的不同类型
- 将 std::set 与基于键的比较器一起使用
- 带自定义比较器的最小优先级队列
- 函数类作为比较器
- 优先级队列自定义比较器
- 什么是自定义比较器以及如何在 C++ 的排序函数中使用它?
- 没有默认构造函数作为模板参数的自定义比较器
- set_intersection使用自定义设置比较器
- 如何为集合 c++ 建立比较器
- C++复杂情况的比较器通过参数问题
- 对于BTreeMap和其他依赖于Ord的东西,是否有等效于C++比较器对象?
- 对没有比较器或λ函数的向量进行排序?
- "operator()"在重载运算符方法中是什么意思,在priority_queue(STL)中用作C++中的比较器?
- 用户定义的结构是否有默认C++比较器?
- C++设置了一个用于排序的比较器和另一个用于唯一性的比较器
- 使用迭代器的自定义比较器函数
- C++对λ比较器EXC_BAD_ACCESS进行排序
- 如果要求比较器是严格的总排序,而不仅仅是严格的弱排序,C++标准算法会更快吗?
- STL:用未构造的有状态比较器初始化容器