1

私はstd::generateを初めて使用し、それを使用してベクトルを初期化するプログラムを構築しようとしました。しかし、それは私の期待とは異なった振る舞いをしています。

私は抽象的な基本クラスを持っています:

template <typename G>
class RandomAllele {
 public:
  RandomAllele() { /* empty */ }
  virtual ~RandomAllele() { /* empty */ }

  virtual G operator()() const = 0;
}; 

これは(たとえば)によって拡張されます:

class RandomInt : public RandomAllele<int> {
 public:
  RandomInt(int max) : max_(max) {}
  int operator()() const { return rand() % max_; }
 private:
  int max_;
};

継承クラスのインスタンスをポインタでファクトリクラスに渡し、それをstd :: generate:の3番目の引数として使用します。

template<typename G, typename F>
class IndividualFactory {
 public:
  IndividualFactory(int length, const RandomAllele<G> *random_allele)
      : length_(length), random_allele_(random_allele) { /* empty */ }

  individual_type *generate_random() const {
    std::vector<G> *chromosome = new std::vector<G>(length_);
    std::generate(chromosome->begin(), chromosome->end(), *random_allele_); */

    return new individual_type(chromosome);
  }
 private:
  int length_;
  RandomAllele<G> *random_allele_;
};

RandomAlleleは抽象クラスであるため、インスタンス化できないというエラーが表示されます。ポインタがすでに存在するのに、なぜgenerateはそれをインスタンス化する必要があるのですか?また、継承クラスRandomIntの代わりに基本クラスを使用しようとしているのはなぜですか?

std :: generateを:に置き換えると、これは正常に機能します。

for(auto iter = chromosome->begin(); iter != chromosome->end(); ++iter)
  *iter = (*random_allele_)();

しかし、なぜそれが奇妙に動作するのかを理解したいので、これを行う方法がある場合は、generateを使用したいと思います。

御時間ありがとうございます、

リス

4

5 に答える 5

4

他の人が上で述べたように、generateandgenerate_n関数はジェネレータオブジェクトを値で取得するため、このコンテキストで継承を直接使用することはできません。

ただし、実行できる1つのトリックは、ソフトウェアエンジニアリングの基本定理を適用することです。

間接参照の別のレイヤーを追加することで、問題を解決できます

ポリモーフィックファンクターを直接渡すのではなく、このポリモーフィックファンクターへのポインターを格納するラッパーファンクターを渡し、呼び出しを適切に転送します。例えば:

template <typename T> class IndirectFunctor {
public:
    explicit IndirectFunctor(RandomAllele<T>* f) : functor(f) {
         // Handled in initializer list
    }

    T operator() () const {
        return (*functor)();
    }

private:
    RandomAllele<T>* functor;
};

次に、このオブジェクトをに渡すと、次generateのようになります。

RandomAllele<T>* functor = /* ... create an allele ... */
std::generate(begin, end, IndirectFunctor<T>(functor));

その後、すべてが意図したとおりに機能します。これは、IndirectFunctor<T>値でコピーする場合は、格納されているポインタを浅くコピーするだけで、RandomAllele呼び出したいポインタを指すためです。RandomAlleleこれにより、基本クラスポインタを介して型のオブジェクトを直接コピーしようとしないため、発生したスライスの問題が回避されます。常にラッパーオブジェクトをコピーし、複製を試みませんRandomAllele

お役に立てれば!

于 2011-02-15T23:54:51.067 に答える
2

std :: generateのジェネレーターは値によって渡されるため、コピーされます。

于 2011-02-15T23:44:12.550 に答える
2

一般に、C ++標準ライブラリは静的ポリモーフィズム(テンプレート)を実装し、関数オブジェクトのランタイムポリモーフィズム(仮想メソッド)をサポートしていません。これは、すべての関数オブジェクトを値で渡すためです。これらはステートレスまたはほぼステートレスであると想定しているため、ポインターまたは参照による受け渡しの追加の間接参照は、値よりもコストがかかります。

値によって渡されるため、これによりスライスが発生し、を使用しようとすると、実際に指している派生型ではなく、正確なクラスRandomAllele<G>を意味していると見なされます。必要なジェネレーターファンクタータイプのテンプレートだけを直接テンプレート化するのではなく。G

于 2011-02-15T23:48:00.230 に答える
0

プロトタイプは次のとおりです。

template <class ForwardIterator, class Generator>
void generate ( ForwardIterator first, ForwardIterator last, Generator gen );

したがって、genは値によって渡されるため、コンパイラはコピーによってRandomAlleleを構築しようとします。したがって、問題が発生します。

解決策は、エンベロープを使用して必要な間接参照を提供することです。

template<class G>
class RandomAlleleEnvelope
{
public:
    RandomAlleleEnvelope(const RandomAllele<G>* ra)
        : ra_(ra)
    {}
      int operator()() const { return (*ra_)(); }
private:

    const RandomAllele<G>* ra_;
};

  std::generate<std::vector<int>::iterator,RandomAlleleEnvelope<int> >(chromosome->begin(), chromosome->end(), random_allele_); 

また、別の解決策があることに注意してください。参照を使用するために独自の生成を定義します。

template <class ForwardIterator, class Generator>
  void referenceGenerate ( ForwardIterator first, ForwardIterator last, 
                           const Generator& gen )
{
  while (first != last)  *first++ = gen();
}
 referenceGenerate(chromosome->begin(), chromosome->end(), *random_allele_); 

また、次のことが機能するはずだと思います。つまり、標準の生成を使用して、参照型を明示的に処理するようにします。

std::generate<std::vector<int>::iterator, const RandomAllele<int>& >
                   (chromosome->begin(), chromosome->end(), *random_allele_); 

これは失敗するので、VS2010でインスタンス化する必要があります。一方、自分で定義できる場合は、次のようになります。

  template <class ForwardIterator, class Generator>
  void myGenerate ( ForwardIterator first, ForwardIterator last, Generator gen )
  {
     while (first != last)  *first++ = gen();
  }
  myGenerate<std::vector<int>::iterator, const RandomAllele<int>& >
        (chromosome->begin(), chromosome->end(), *random_allele_); 

VS2010は、std ::generateが別のstd::generateの用語であり、デフォルトで非参照パラメーターに戻るため、失敗します。

于 2011-02-15T23:46:52.600 に答える
0

問題は、すべての標準アルゴリズムが、従来のC制約に準拠するために、引数を値で受け取ることです。したがって、ここでstd::generate()アルゴリズムはファンクターを値で取得します。タイプのファンクターRandomAllele<int>は抽象タイプです。はい、それは具象型を指すポインタですが、ポインタは抽象型です。このオブジェクトをコピーする際、アルゴリズムはRandomAllele<int>;のコピーコンストラクターを呼び出します。つまり、アルゴリズムは抽象型のインスタンスを構築します。そして、これはC++言語で禁止されていることです。

次のように、ランタイム環境にあまり心配しないように指示できます。

RandomInt *cp = dynamic_cast<RandomInt*>(random_allele);
if( ! cp ) {
    // i thought the pointer is of RandomInt. It isn't. Err.
    std::terminate(); // or something
}
std::generate(chromosome->begin(), chromosome->end(), *cp);
于 2011-02-15T23:56:56.100 に答える