我是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。
感谢您的时间,
里斯
答案 0 :(得分:4)
正如其他人在上面提到的那样,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
。
希望这有帮助!
答案 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);