3

私は、このソリューションに大まかに基づいて、一連のn次元デカルト積クラスに取り組んでいます。

同じ基本的なアルゴリズムのセットに対してさまざまなデータ型があり、「ああ!テンプレートを使用して全体的な作業を減らす」と思いました。そして、これまで、それは素晴らしい働きをしてきました。Boostのiterator_facadeを使用しています。

私の問題は、で動作するように作成した派生クラスにありmap<char, boost::integer_range<int> >ます。反復ごとにが生成map<char,int>されますが、アルゴリズムに関する限り、スペースが無駄になっているため、2番目の値が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は除外されません。ただし、それ以上の反復は行います。

これはテンプレートと派生クラスへの私の最初の進出であるため、明らかな何かが欠けていると確信していますが、私の人生ではそれを理解することはできません。GCC4.6.3とClang3.0-6は同じ出力を提供します。

やめろ!

編集:また、私はC++プログラミングに比較的慣れていません。批評、スタイル、その他の批評があれば、私はそれを受け入れます。ありがとう!

4

2 に答える 2

5

実際には、ポインターとは何の関係もありません。代わりに、virtual機能の制限です。

構築および破棄中、仮想呼び出しは、派生した対応するクラスに対する基本クラスの構築および破棄の順序のため、純粋に仮想にすることはできず、代わりに静的に解決されます。

つまりvirtualfunc、 のコンストラクタまたはデストラクタでのへの呼び出しは、 Base(どういうわけか) に解決され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);
};

これはBaseDerived1予想とは異なり、出力されます。これには正当な理由があります。物事の構築順序を覚えておいてください。

  • Derived()と呼ばれる
  • 自動的に呼び出しますBase()
  • 呼び出します_i(i)
  • の本体に入りますがDerived()、何もしません

したがって、Base()が呼び出されたとき、 は_iまだ初期化されていないため、 を呼び出すのcallはばかげています。ありがたいことに、標準は、ほとんどの場合、最初に期待するものではありませんが、ここで適切な回避策を提供するのに適していると考えています.

于 2012-05-22T17:23:22.727 に答える
3

基本クラスCartProductIterator2DMapには、仮想関数を呼び出すコンストラクターがあります。

CartProductIterator2DMap(const Container2DMap &container) {
    rangeIterSetup(container);
}

この仮想関数は最終的に別の仮想関数を呼び出します: genReturnValue.

ここで、基本クラスのコンストラクターが実行されているとき、派生クラスはまだ構築されていません。したがって、genReturnValue基本クラスのコンストラクターからの呼び出しは、 を呼び出しますCartProductIterator2DMap::genReturnValue。ただし、後続の呼び出し (オブジェクトが完全に構築された後) では、次の呼び出しが行われますDerivedCPIterator::genReturnValue

基本クラスのコンストラクターが仮想関数を呼び出さないようにする必要があります。

于 2012-05-22T17:25:07.047 に答える