3

Well I'm currently refactoring a class I made quite a long time ago. The class is a container type.

Many functions can use advantage of the class structure, and are thus implemented as member functions. However it now seems to be a lot of function which seem "the same", ie a "find" function:

iterator find(ITEM)
const_iterator find(ITEM) const;
iterator find_if(ITEM, PRED)
const_iterator find_if(ITEM, PRED) const;

4 "functions" to describe nearly the same (and code is almost the same in each version). This becomes very tedious when updating the class, I have to make sure each version is upgraded. Is there a manner to handle these things better? Some other function in the class CAN possibly take 2 predicates, and that meant I suddenly had 8 functions to manage.

I tried calling the "non-constant version from the constant version" but that obviously doesn't work.

So how does one handle these things? Just bite the bullet and write it down?

EDIT: just to inform: my datastructure resembles a "tree". Each "object" contains the data (which the find searches) and a "list" with sub-trees. The find function works recursive over all subtrees of a tree (and sub-sub trees). - Just like one would expect when searching a tree.

As there isn't a clear "end" or "start" iterator to such a tree, using std::find doesn't yield the correct functionality.

fredoverflow
  • 237,063
  • 85
  • 359
  • 638
paul23
  • 7,226
  • 9
  • 44
  • 108
  • 2
    @FredOverflow: Great point. Iterators and algorithms exist precisely to solve the problem that the OP wants to roll again himself. – Kerrek SB Dec 27 '11 at 14:38
  • 1
    @FredOverflow, Kerrek: Not exactly. Yes, `std::find` exists, but so do `std::map::find`, `std::set::find`, etc. – Oliver Charlesworth Dec 27 '11 at 14:48
  • 1
    @OliCharlesworth: But no `map::find_if` :-) Yes, when appropriate provide a member function, but not if it only does the same thing as the generic algorithm. – Kerrek SB Dec 27 '11 at 14:52
  • @KerrekSB (and anyone) - well I gave some clarification as to why I use a member method-find. (Maybe I ought to rename it, though from looking outside the class it makes perfect sense). – paul23 Dec 27 '11 at 14:57
  • I can see why member-find is useful (you're essentially reinventing `std::set`). I don't see why you need a custom `find_if`, and I also don't see why you can't provide a normal iterator interface with begin and end. – Kerrek SB Dec 27 '11 at 14:59
  • Every container should have `begin()` and `end()` methods, regardless of implementation. `std::set` is also a tree and does have them, so why can't your tree have them? – fredoverflow Dec 27 '11 at 15:54

2 Answers2

8

Scott Meyers solved a similar problem in Item 3 of Effective C++ by calling the const version inside the non-const version and then casting the const result back to non-const:

const_iterator find(const T& value) const
{
    // actual implementation of find
}

iterator find(const T& value)
{
    return const_cast<iterator>(static_cast<const container*>(this)->find(value));
}
fredoverflow
  • 237,063
  • 85
  • 359
  • 638
2

If you're willing to use the C++11 standard, this pattern (using a single template iterator class and two typedefs) might be of interest: http://www.sjvs.nl/c-implementing-const_iterator-and-non-const-iterator-without-code-duplication/

Bas van Schaik
  • 281
  • 1
  • 3
  • 7