如何在调用其他函数的const和非const函数之间重用代码

时间:2014-09-25 00:16:11

标签: c++ templates const

在此示例代码中,两个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;
}

请注意,这不是一个重复的问题。以下类似的问题和答案是不同的:

  • 123758 - 仅涵盖返回值的简单函数,而此函数调用其他函数,因此那里给出的解决方案不适用于此问题
  • 23809745 - 同样的问题,只涵盖返回值的简单函数,答案不适用于此问题

如果我尝试在这些答案中给出的解决方案,它不起作用,但看起来像这样:

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()函数中删除重复的循环)而不是关于设计的注释,那么将非常感激。如果它在过程中删除了重复的循环,那么设计当然没问题。

4 个答案:

答案 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);
    }
}