C Cartesian产品迭代器在第一次迭代时调用基类函数
作者:互联网
我正在开发一组n维笛卡尔积类产品,基于这个solution.
对于相同的基本算法集,我有许多不同的数据类型,我想“啊哈!我将使用模板来减少我的整体工作!”而且,到目前为止,它一直很棒.我正在使用Boost的iterator_facade.
我的问题是我使用map< char,boost :: integer_range< int>处理的派生类.取代.每次迭代产生一个映射< char,int>,但我排除第二个值为0的对,因为就我的算法而言,它们只是浪费空间.
派生类重载生成迭代器的返回值的函数,并且它可以工作.但是,在第一次迭代期间,将调用基类的生成器.我很困惑.有没有人有任何想法?
以下是相关的代码段:
#include <boost/container/flat_map.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/irange.hpp>
#include <utility>
#include <iostream>
using namespace boost;
using namespace boost::tuples;
using namespace std;
template <class Container2DMap,
class return_type = boost::container::flat_map<typename Container2DMap::value_type::first_type,
typename Container2DMap::value_type::second_type::value_type> >
class CartProductIterator2DMap : public boost::iterator_facade<
CartProductIterator2DMap<Container2DMap, return_type>,
const return_type,
boost::forward_traversal_tag> {
public:
typedef typename Container2DMap::value_type::first_type first_type;
typedef typename Container2DMap::const_iterator first_iterator;
typedef typename Container2DMap::value_type::second_type::value_type second_type;
typedef typename Container2DMap::value_type::second_type::const_iterator second_iterator;
CartProductIterator2DMap(const Container2DMap &container) {
rangeIterSetup(container);
}
CartProductIterator2DMap() : _finished(true) {}
virtual ~CartProductIterator2DMap() {}
private:
virtual bool equal(const CartProductIterator2DMap &other) const {
if (_finished || other._finished) {
if (_finished && other._finished) {
return true;
} else {
return false;
}
} else if (_currentIter == other._currentIter) {
return true;
} else {
return false;
}
}
virtual void increment() { advance(); }
virtual void advance() {
advanceIter();
}
virtual const return_type& dereference() const { return _currentIter; }
protected:
struct mode {
const static bool stopIter = false;
const static bool continueIter = true;
};
typedef boost::tuple<second_iterator,
second_iterator,
second_iterator> SecondIterDescription;
typedef boost::container::flat_map<first_type, SecondIterDescription> RangeIterMap;
friend class boost::iterator_core_access;
return_type _currentIter;
RangeIterMap _rangeIter;
bool _finished;
bool _iterMode;
virtual void advanceIter() {
if (_iterMode == mode::continueIter) {
_currentIter = genReturnValue(_rangeIter);
_iterMode = advanceRangeIter(_rangeIter);
} else {
_finished = true;
}
}
virtual void rangeIterSetup(const Container2DMap &container) {
_finished = false;
if (container.empty()) {
_iterMode = mode::stopIter;
} else {
_iterMode = mode::continueIter;
for (typename Container2DMap::const_iterator it = container.begin();
it != container.end(); ++it) {
_rangeIter.insert(
make_pair(it->first,
SecondIterDescription(it->second.begin(), it->second.end(), it->second.begin())
)
);
}
advance();
}
}
virtual return_type genReturnValue(const RangeIterMap &rangeIter) {
std::cout << "Calling base class." << std::endl;
return_type returnValue;
for( typename RangeIterMap::const_iterator it = rangeIter.begin();
it != rangeIter.end(); ++it) {
returnValue.insert(
make_pair(it->first, *get<2>(it->second))
);
}
return returnValue;
}
virtual bool advanceRangeIter(RangeIterMap &rangeIter) {
for (typename RangeIterMap::iterator it = rangeIter.begin(); ; ) {
++(get<2>(it->second));
if (get<2>(it->second) == get<1>(it->second)) {
if (it + 1 == rangeIter.end()) {
return mode::stopIter;
} else {
// cascade
get<2>(it->second) = get<0>(it->second);
++it;
}
} else {
// normal break point
return mode::continueIter;
}
}
return mode::continueIter;
}
};
typedef boost::integer_range<int> _intRange;
typedef boost::container::flat_map<char, _intRange> CharRange;
typedef boost::container::flat_map<char, int> ResidueCount;
template <class Container2D,
class return_type = boost::container::flat_map<typename Container2D::value_type::first_type,
typename Container2D::value_type::second_type::value_type> >
struct BaseIterContainer {
typedef CartProductIterator2DMap<Container2D, return_type> const_iterator;
const Container2D &_container;
BaseIterContainer( const Container2D &container) : _container(container) {}
const_iterator begin() const { return const_iterator(_container); }
const_iterator end() const { return const_iterator(); }
};
typedef BaseIterContainer<CharRange, ResidueCount> BaseCharRangeIter;
typedef CartProductIterator2DMap<CharRange, ResidueCount> BaseCPIterator;
class DerivedCPIterator : public BaseCPIterator {
public:
DerivedCPIterator() : BaseCPIterator() {}
DerivedCPIterator(const CharRange & charRange) : BaseCPIterator(charRange) {}
protected:
ResidueCount genReturnValue(const RangeIterMap &rangeIter) {
std::cout << "Calling derived class." << std::endl;
ResidueCount returnValue;
for( RangeIterMap::const_iterator it = rangeIter.begin();
it != rangeIter.end(); ++it) {
const char aa = it->first;
const int aaCount = *get<2>(it->second);
if (aaCount > 0) {
returnValue.insert(
make_pair(aa, aaCount)
);
}
}
return returnValue;
}
};
struct DerivedCharRangeIter {
typedef DerivedCPIterator const_iterator;
const CharRange &_container;
DerivedCharRangeIter( const CharRange &container) : _container(container) {}
const_iterator begin() const { return const_iterator(_container); }
const_iterator end() const { return const_iterator(); }
};
std::ostream& operator<<(std::ostream& out, const ResidueCount &rCount) {
foreach(const ResidueCount::value_type& aaCount, rCount) {
char aa = aaCount.first;
int totalAACount = aaCount.second;
out << "(" << aa << "," << totalAACount << ")";
}
return out;
}
int main(int argc, char **argv) {
cout << "Base Container" << endl;
CharRange test;
test.insert(make_pair('a', _intRange(0, 3)));
test.insert(make_pair('b', _intRange(0, 3)));
BaseCharRangeIter t(test);
BaseCharRangeIter::const_iterator it = t.begin();
for( ;it != t.end(); ++it) {
cout << *it << endl;
}
cout << endl;
cout << "Derived Container: " << endl;
DerivedCharRangeIter r(test);
DerivedCharRangeIter::const_iterator rt = r.begin();
for( ; rt != r.end(); ++rt) {
cout << *rt << endl;
}
return 0;
}
结果我得到:
Base Container
Calling base class.
(a,0)(b,0)
Calling base class.
(a,1)(b,0)
Calling base class.
(a,2)(b,0)
Calling base class.
(a,0)(b,1)
Calling base class.
(a,1)(b,1)
Calling base class.
(a,2)(b,1)
Calling base class.
(a,0)(b,2)
Calling base class.
(a,1)(b,2)
Calling base class.
(a,2)(b,2)
Derived Container:
Calling base class.
(a,0)(b,0)
Calling derived class.
(a,1)
Calling derived class.
(a,2)
Calling derived class.
(b,1)
Calling derived class.
(a,1)(b,1)
Calling derived class.
(a,2)(b,1)
Calling derived class.
(b,2)
Calling derived class.
(a,1)(b,2)
Calling derived class.
(a,2)(b,2)
每个genReturnValue每次调用都会打印它的类(基类或派生类).基类的功能应该如此.但是,派生类没有.第一次迭代调用基类genReturnValue,并且不过滤掉0.但是,进一步的迭代确实如此.
由于这是我第一次涉足模板和派生类,我敢肯定我错过了一些明显的东西,但对于我的生活,我无法弄明白. GCC 4.6.3和Clang 3.0-6提供相同的输出.
HALP!
编辑:另外,我在C编程方面相对较新.如果你有任何批评,风格或其他方面,我愿意接受.谢谢!
解决方法:
实际上它与指针无关;但相反,它是虚函数的限制.
在构造和销毁期间,虚拟调用不能纯粹是虚拟的,因为基类的构造和销毁的顺序相对于它们的派生对应物而言,因此它们是静态解析的.
也就是说,在构造函数或Base的析构函数中调用virtualfunc(以某种方式)解析为Base :: virtualfunc.如果这是纯虚函数,则会出现未定义行为.
因此,一般准则是:
>永远不要从构造函数或析构函数中调用虚函数.
例:
struct Base {
Base() { call(); }
virtual call() { std::cout << "Base\n"; }
};
struct Derived: Base {
Derived(int i): _i(i);
virtual call() { std::cout << "Derived" << _i << "\n"; }
int _i;
};
int main() {
Derived d(1);
};
这将打印Base,而不是Derived1,如您所料;并且有一个很好的理由:记住事物的构造顺序:
>调用Derived()
>它自动调用Base()
>它叫_i(i)
>它进入Derived()的主体,它什么都不做
因此,当调用Base()时,_i尚未初始化,因此调用call将是愚蠢的.值得庆幸的是,标准已经认为适合在这里提供一个好的解决方案,即使它在大多数情况下不是你想要的.
标签:cartesian-product,derived-class,c,templates 来源: https://codeday.me/bug/20190726/1538784.html