这一直是我脑海中的一个问题的可能解决方案,但是因为它是一个相当明显的技术违反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&lt;&gt; :: my_sorter传递给SortedBase :: m_codeList的构造函数。但是因为有状态的分类器在子级中,显然不是在构造STL集的时候构造的。
如果我不在任何构造函数的m_codeList中进行任何插入或搜索,我想知道这是否是一个问题。
有状态分拣机免责声明
我正式ASSERT(),任何有状态排序仿函数使用的规则只有在它控制的STL容器为空或者不久之后才会清除()后才会改变。
答案 0 :(得分:0)
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
基类中,使用成员基础成语。
此:
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;
};