簡體   English   中英

如何將std :: generate / generate_n與多態函數對象一起使用?

[英]How can I use std::generate/generate_n with a polymorphic function object?

我是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的第三個參數:

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。

謝謝你的時間,

里斯

正如其他人在上面提到的那樣, generategenerate_n函數按值獲取其生成器對象,從而使您無法直接在此上下文中使用繼承。

但是,您可以采取的一項技巧是應用軟件工程的基本定理:

任何問題都可以通過添加另一層間接解決

而不是直接傳遞多態函子,而是傳遞包裝函子,該函子存儲指向該多態函子的指針,然后適當地轉發調用。 例如:

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

希望這可以幫助!

std :: generate的生成器按值傳遞,因此被復制。

通常,C ++標准庫實現靜態多態性(模板),並且不支持函數對象的運行時多態性(虛擬方法)。 這是因為它按值傳遞其所有函數對象,並假設它們是無狀態的或幾乎無狀態的,這樣,通過指針或引用傳遞的附加間接調用將比按值花費更大。

由於它按值傳遞,因此會導致切片,並且當您嘗試使用RandomAllele<G>它認為您的意思是確切的類而不是它實際指向的任何派生類型。 您可以直接使用所需的確切發電機函子類型的模板作為模板,而不必在G上進行模板化。

原型是:

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_); 

我還認為以下方法應該起作用,即使用標准的generate並顯式地使其處理引用類型:

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的術語,默認返回非引用參數。

問題是所有標准算法都按值采用其參數,以符合傳統的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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM