[英]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。
謝謝你的時間,
里斯
正如其他人在上面提到的那樣, generate
和generate_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.