Consider this simplified example:
#include <list>
typedef std::list<int> IntList;
class KindaIntList {
public:
IntList::const_iterator begin() const { /* do some stuff */ }
IntList::const_iterator end() const { /* do some stuff */ }
// ...etc
};
The KindaIntList class implements some of the methods of the STL list.
Now, I have a function
void f(IntList l) {
// do stuff
}
which only calls methods that are implemented by KindaIntList. I would like to be able to call it with an IntList or with a KindaIntList argument. Is that possible?
I thought about using templates, but the definition of f is quite large and I don’t want to put it in a header file (f is a member of a class, and I don’t want it to be inlined).
Edit
The function f is actually a virtual member of another class; so I’m not sure how to make it into a template member.
Despite your misgivings about templates, this really is an appropriate spot to use C++ templates. Template functions perfectly capture the notion of “this function works with any arguments, as long as the operations I perform on those arguments are well-defined.”
You don’t need to worry about inlining in this case. Unless you define
finside of the body of a class, it won’t automatically be inlined, even if it’s a template. For example, in this code:Because
fisn’t defined inside of theMyClassbody, it’s not considered an inline function.As for your concern about making the header file too large, I contend that this really isn’t something to worry about. If you’re worried about making the header too large, you can either put a big comment about halfway down saying something like
Alternatively, you could make a separate
.hfile for the template implementation, then#includethat file at the bottom of the header file. This shields the client from seeing the template implementations unless they actively go looking for it.Hope this helps!
EDIT: If
fis virtual, then you cannot make it a template function (as you’ve probably figured out). Consequently, if you want to make it work for “things that happen to look likestd::list,” then you don’t have many good options. Normally you’d create a base class for bothstd::listand your custom list type, but this isn’t an option as you can’t modifystd::list.Fortunately, there is a way to treat
std::listand things that look like it polymorphically using a trick called external polymorphism. The idea is that while you can’t make the appropriate classes behave polymorphically, you can add an extra layer of indirection around those objects by introducing a polymorphic class hierarchy that just forwards all its requests to the objects that themselves are not polymorphic.If you’re willing to pull out the Big Template Guns, you can encapsulate this logic inside of a class that works much the same way as the new
std::functiontemplate type. The idea is as follows. First, we’ll create a polymorphic base class that exports all the functions you want to call as pure virtual functions:Now, we can define a template subclass of
Listthat implements all of the public interface by forwarding all of the calls to an object of the actual type. For example:Now that you have this wrapper, you can implement
fso that it takes in aList:And you can call this function on an object that looks like a list by first wrapping it in an object of type
ListImpl. For exmaple:Of course, this is bulky and unwieldy. You also have to worry about resource leaks, which aren’t very fun. Fortunately, you can solve this by wrapping up all the logic to deal with
ListandListImplin a helper class, like this one here:You can now write
fto take in aListWrapperlike this:(This assumes that you’ve updated
ListandListImplwith a virtualclonefunction that makes a copy of the object, which I’ve omitted for brevity’s sake).And magically, this code is now legal (and safe!):
This code works because the template constructor for
ListWrapperwill automatically infer the type of its argument and implicitly create an object of typeListImplappropriate for that object. It also encapsulates the memory management for you, so you never see any explicitnews ordeletes. Moreover, it means that you can pass in any object that you’d like and everything will work automatically – we’ve essentially made anything that looks like alistpolymorphic by using a parallel class hierarchy!Whew! That was fun! Hope this helps!