@unspoken,
@JLBorges knows the stuff here. Engineering is all about setting up experiments and collecting data to prove what's best.
Consider what your code proposes:
1 2 3 4 5 6
|
class DifferentClass
{
private:
void FunctionToCall(){}
MyClass<DifferentClass::FunctionToCall()> my_class; //How to declare my_class properly?
};
|
It's not just about declaring a function, because to call a member function (what you're calling a method) requires an instance of DifferentClass.
By the time you solve that problem you end up with a start on what std::bind is.
In order to be fully generic you have to deal with what bind does for parameters, for the instance, for the pointer to the member function....aaaaannnnndd you're almost done rebuilding bind.
If performance is an absolute priority, the best you're going to manage is to figure out how to remove the obstacles, like the instance (not likely), or the pointer to member function (which can invoke a double de-reference kind of behavior, something akin to a virtual function call).
Consider how std::sort does this. It takes a function object. That can expand into inline code. If performance is absolute, that may be the kind of direction you'd have to take to really have an impact, and it might be considerable depending on how much nested calls might be removed.
This means moving away from that generic notion of a container which holds a collection of callable objects (all of which involve something of a pointer to a function paradigm, or a call to a member function through an instance) into a composition of code through the same idea as std::sort's comparison function object.
Eigen (the linear algebra library) does something like this from a unique perspective. For Eigen, a statement like:
a = b * c;
Where c is a matrix, b is a vector and a is a vector result...this doesn't actually perform the multiplication of a vector by a matrix at "*", as we'd expect from a common C++ pattern. Instead, the "*" operator receives an instruction code, not a matrix, which feeds to "=" an instruction code resulting in a run time optimized output. The actual execution of the multiplication isn't started until execution enters the "=" operator function.
Depending on how your application code calls these callable objects, you may need to shift perspective in a similar (very loosely related) way, such that you can arrange to couple function objects instead of callable objects, so the code can be constructed inline.
Perhaps policy based template design...I'm guessing here, because I only see the callable object problem, not the overall application level code you're working on, and therefore have no idea why a callable object collection would be the design choice.
But back to @JLBorges point, and what I'm echoing, is that quite often such "early optimization" thoughts are more off course than really matters.
Taking the point from another perspective (on the subject of performance), I had a long running exchange (almost an argument) about someone of the belief that assembler had to be faster than C++ because it was lower level. Yet, the level isn't what makes a difference, at least not in the 21st century with the current (and likely up and coming) optimizing compilers. Sometimes a higher level expression can lead to even better performance, which I admit sounds counter intuitive at first. std::sort is Stroustrup's favorite (or once favorite) demonstration of how the opposite can be true, and for a fundamental reason (it's inline opportunity from the function object). This happens because the design gives the optimizer more information that does C's qsort function, which requires a function pointer to a comparison function.
The same basic principle is under what I'm trying to convey relative to your inquiry. We all know that early optimization is generally recognized as an evil, derailing productivity. However, as Stroustrup points out in one of his older works, sometimes it isn't an evil, because sometimes it's about design.
We also know that minor optimization adjustments produce minor results (with occasional bumps that thrill), whereas algorithmic changes usually produce much more dramatic results.
This becomes my point here. If you can refashion the way calling code uses these callable objects, maybe you can fashion a different design which can more frequently take advantage of function objects instead of callable objects, thereby giving the optimizer more information from an expression resulting in better optimized performance.
Sometimes such ideas require major refactoring, dropping previously held conventions and ultimately leading to a dead end, where we accept that something like std::bind and std::function have been engineered to within a few % of an "in house" solution, with less work on our part.