如何将std :: generate / generate_n与多态函数对象一起使用?

时间:2011-02-15 23:37:39

标签: c++ polymorphism std 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无法实例化,因为它是一个抽象类。为什么生成需要在指针已存在时实例化它?为什么它试图使用基类而不是继承类RandomInt?

如果我将std :: generate替换为:

,这样可以正常工作
for(auto iter = chromosome->begin(); iter != chromosome->end(); ++iter)
  *iter = (*random_allele_)();

但是我仍然希望理解为什么它表现得很奇怪,如果有办法,我宁愿使用generate。

感谢您的时间,

里斯

5 个答案:

答案 0 :(得分: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

希望这有帮助!

答案 1 :(得分:2)

std :: generate的生成器按值传递,因此被复制。

答案 2 :(得分:2)

通常,C ++标准库实现静态多态(模板),不支持函数对象的运行时多态(虚方法)。这是因为它通过值传递其所有函数对象,假设它们是无状态的或几乎无状态的,这样通过指针或引用传递的附加间接将比按值更昂贵。

因为它是按值传递的,所以会导致切片,当你尝试使用RandomAllele<G>时,它认为你的意思是那个确切的类而不是它实际指向的任何派生类型。而不是在G上模板化您直接需要的确切生成器函子类型的模板。

答案 3 :(得分:0)

原型是:

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

因此gen通过值传递,因此编译器尝试通过复制构造RandomAllele,因此问题。

解决方案是使用Envelope提供所需的间接:

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的术语,它默认返回非参考参数。

答案 4 :(得分: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);