2011-02-15 53 views
1

我是std :: generate的新手,並試圖構造一個使用它初始化向量的程序。然而它的表現與我的期望不同。如何在一個多態函數對象中使用std :: generate/generate_n?

我有一個抽象基類:

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無法實例化,因爲它是一個抽象類。爲什麼當指針已經存在時,生成需要實例化呢?爲什麼它試圖使用基類而不是繼承類RandomInt?

如果我更換STD這工作得很好::生成:

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

但我還是想了解爲什麼它的行爲很奇怪,我寧願使用產生是否有辦法做到這一點。

感謝您的時間,

里斯

回答

4

正如其他人已經提到的那樣,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

希望這會有所幫助!

2

的std ::生成的生成是按值傳遞,因此複製。

+0

那麼,爲什麼將它複製的基類,而不是我傳遞值嗎? – 2011-02-15 23:47:16

+0

因爲您傳遞的值的類型是RandomAllele 。 – Erik 2011-02-15 23:52:14

0

原型爲:

template <class ForwardIterator, class Generator> 
void generate (ForwardIterator first, ForwardIterator last, Generator 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失敗::產生是另一種性病方面::產生其默認返回非引用參數。

+0

@ Keith-你需要在這種情況下顯式指定模板參數嗎?我認爲編譯器可以自動推斷出來。 – templatetypedef 2011-02-16 00:07:45

2

通常,C++標準庫實現靜態多態性(模板),並且不支持函數對象的運行時多態(虛擬方法)。這是因爲它通過值傳遞它的所有函數對象,假設它們是無狀態的或者幾乎是無狀態的,這樣增加了通過指針或引用傳遞的間接方式會比值更昂貴。

,因爲它是按值傳遞的,這導致切片,當您嘗試使用RandomAllele<G>它認爲你的意思是,準確的類沒有任何派生類型,它實際指向。而不是在G上模板,而是直接在您想要的確切生成器仿函數類型上進行模板化。

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); 
相關問題