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 的第三个参数:

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函数按值获取它们的生成器对象,从而使您无法在此上下文中直接使用继承。

但是,您可以做的一个技巧是应用软件工程基本定理:

任何问题都可以通过添加另一层间接来解决

与其直接传入多态函子,不如传入一个包装函子,该函子存储指向该多态函子的指针,然后适当地转发调用。例如:

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

由于它是按值传递的,因此会导致切片,当您尝试使用 aRandomAllele<G>时,它认为您的意思是确切的类,而不是它实际指向的任何派生类型。而不是G直接在您想要的确切生成器仿函数类型上的模板上进行模板化。

于 2011-02-15T23:48:00.230 回答
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 的术语,默认返回为非引用参数。

于 2011-02-15T23:46:52.600 回答
0

问题是所有标准算法都按值取参数,以符合传统的 C 约束。所以这里的std::generate()算法按值取函子。您的 type 函子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 回答