在此示例代码中,两个process()
函数内的循环是重复的。唯一的区别是一个是const
而另一个不是。
有没有办法删除代码重复,这样循环只存在一次?这只是一个例子,但在实际代码中,循环非常复杂,因此出于维护原因,我只希望循环存在一次。
#include <iostream>
#include <vector>
typedef unsigned int Item;
typedef std::vector<Item *> Data;
struct ReadOnlyAction {
void action(const Item *i)
{
// Read item, do not modify
std::cout << "Reading item " << *i << "\n";
}
};
struct ModifyAction {
void action(Item *i)
{
// Modify item
std::cout << "Modifying item " << *i << "\n";
(*i)++;
}
};
void process(Data *d, ModifyAction *cb) {
// This loop is actually really complicated, and there are nested loops
// inside it three levels deep, so it should only exist once
for (Data::iterator i = d->begin(); i != d->end(); i++) {
Item *item = *i;
cb->action(item);
}
}
void process(const Data *d, ReadOnlyAction *cb) {
// This is the same loop as above, and so the code should not be duplicated
for (Data::const_iterator i = d->begin(); i != d->end(); i++) {
const Item *item = *i;
cb->action(item);
}
}
void incrementData(Data *d) {
// Here we modify the pointer, and need to loop through it
ModifyAction incrementItem;
process(d, &incrementItem);
}
void saveData(const Data *d) {
// Here we aren't allowed to modify the pointer, but we still need
// to loop through it
ReadOnlyAction printItem;
process(d, &printItem);
}
int main(void)
{
Data d;
// Populate with dummy data for example purposes
unsigned int a = 123;
unsigned int b = 456;
d.push_back(&a);
d.push_back(&b);
incrementData(&d);
saveData(&d);
return 0;
}
请注意,这不是一个重复的问题。以下类似的问题和答案是不同的:
如果我尝试在这些答案中给出的解决方案,它不起作用,但看起来像这样:
template <class CB>
void processT(const Data *d, CB *cb) {
// Single loop in only one location
for (Data::const_iterator i = d->begin(); i != d->end(); i++) {
const Item *item = *i;
// Compilation fails on the next line, because const Item* cannot be
// be converted to Item* for the call to ModifyAction::action()
cb->action(item);
}
}
void process(const Data *d, ReadOnlyAction *cb) {
processT(d, cb);
}
void process(Data *d, ModifyAction *cb) {
processT(static_cast<const Data *>(d), cb);
}
这是一个简化的示例,因此,如果答案可以集中在问题上(如何从两个process()
函数中删除重复的循环)而不是关于设计的注释,那么将非常感激。如果它在过程中删除了重复的循环,那么设计当然没问题。
答案 0 :(得分:4)
尝试这样的事情:
template <class IteratorType, class CB>
void processT(IteratorType first, IteratorType last, CB *cb)
{
while (first != last)
{
cb->action(*first);
++first;
}
}
void process(const Data *d, ReadOnlyAction *cb)
{
Data::const_iterator first = d->begin();
Data::const_iterator last = d->end();
processT(first, last, cb);
}
void process(Data *d, ModifyAction *cb)
{
Data::iterator first = d->begin();
Data::iterator last = d->end();
processT(first, last, cb);
}
当然,在这个简化的示例中,您可以改为使用std::for_each()
:
#include <algorithm>
void process(const Data *d, ReadOnlyAction *cb)
{
std::for_each(d->begin(), d->end(), &cb->action);
}
void process(Data *d, ModifyAction *cb)
{
std::for_each(d->begin(), d->end(), &cb->action);
}
答案 1 :(得分:1)
看起来如果你将数据作为模板的一部分,就像这样,它会编译......
template <class D, class CB>
void processT(D d, CB *cb) {
for (auto i = d->begin(); i != d->end(); i++) {
auto *item = *i; // this requires c++0x e.g. g++ -std=c++0x
cb->action(item);
}
}
void process(const Data *d, ReadOnlyAction *cb) {
processT(d, cb);
}
void process(Data *d, ModifyAction *cb) {
processT(static_cast<const Data *>(d), cb);
}
编辑 - 也应该没有讨厌的演员表,比如;
void process(Data *d, ModifyAction *cb) {
processT(d, cb);
}
答案 2 :(得分:1)
我会假设你关心的事情是将const*
传递给行动。
template<class Arg, class Data, class Action>
static void process_helper(Data *d, Action *cb) {
for (auto i = d->begin(); i != d->end(); i++) {
Arg item = *i;
cb->action(item);
}
}
void process(Data *d, ModifyAction *cb) {
process_helper<Item*>( d, cb );
}
void process(const Data *d, ReadOnlyAction *cb) {
process_helper<Item const*>( d, cb );
}
现在,如果您缺少C ++ 11,请编写一个特征类:
template<class Data>struct iterator
{ typedef typename Data::iterator iterator; };
template<class Data>struct iterator<const Data>
{ typedef typename Data::const_iterator iterator; };
template<class Arg, class Data, class Action>
static void process_helper(Data *d, Action *cb) {
for (typename iterator<Data>::type i = d->begin(); i != d->end(); i++) {
Arg item = *i;
cb->action(item);
}
}
可以扩展到多个嵌套循环。
答案 3 :(得分:0)
我认为函数process
就像调用相应动作的“代理”。参数的处理以及它们是否为const由该操作拥有。因此,可以像这样简化过程函数(如果c ++ 11已经到位):
template<class DATA, class ACTION>
void process(DATA *d, ACTION *cb){
for (auto item : *d) {
cb->action(item);
}
}