OO's traditional approaches to this are the method template template and the strategy template.
Template method
Firstly, it is an extension of the method described in Vincenzo’s answer: instead of writing a simple non-virtual shell, you are writing a non-virtual function containing the entire algorithm. Those parts that may vary are calls to virtual functions. The specific arguments needed for this implementation are stored in the object of the derived class that provides this implementation.
eg.
class VoxelDrawer { protected: virtual void copy(Coord from, Coord to) = 0;
Strategy
This is similar, but instead of using inheritance, you pass the Copier polymorphic object as an argument to your function. It is more flexible as it separates your various copy strategies from a specific function, and you can reuse your copy strategies in other functions.
struct VoxelCopier { virtual void operator()(Coord from, Coord to) = 0; }; struct SmoothedVoxelCopier: public VoxelCopier {
Although more subtle than passing a pointer to a function, neither a template method nor a strategy will likely have better performance than just passing a pointer to a function: run-time polymorphism is still an indirect function call.
Politics
The modern equivalent of a C ++ strategy strategy template is a policy template. This simply replaces the polymorphism at runtime with a compile-time polymorphism to avoid invoking an indirect function and enable nesting
Due to type inference, you can just call
draw_voxels(arguments, SmoothedVoxelCopier(radius)); draw_voxels(arguments, OtherVoxelCopier(whatever));
NB. I was a bit incompatible here: I used operator() to make my strategy call look like a regular function, but a regular method for my policy. As long as you pick one and stick to it, it's just a matter of taste.
CRTP Template Method
There is one last mechanism, which is a version of the compile-time polymorphism method of the template, and uses the Curiously Recurring Template.
template <typename Impl> class VoxelDrawerBase { protected: Impl& impl() { return *static_cast<Impl*>(this); } void copy(Coord from, Coord to) {...}
Summary
In general, I would prefer a strategy / policy templates for their lower connection and better reuse, and select the template method template only where the top-level algorithm that you parameterize is really set in stone (i.e., either refactoring existing code, either really confident in your analysis of the points of variation), and reuse is really not a problem.
It is also very painful to use the template method if there is more than one axis of variation (i.e. you have several methods, such as copy , and you want to change their implementation independently). You either get code duplication or mixin inheritance.