I have an expensive function defined in a base class, which depends on low level information from its derived classes:
class BaseClass{
...
// Defined in derived class
virtual int low_level(int)=0;
// Expensive function depending on the pure virtual function
void myExpensiveFunction(){
for(...){
for(...){
for(...){
... = low_level(...);
...
}
}
}
}
};
class DerivedClass : public BaseClass{
// A very cheap operation that can be inlined:
inline virtual int low_level(int i){
return a[i];
}
// Calling the base class function
void test(){
myExpensiveFunction();
}
};
If I understand things correctly, the fact that the low-level function is virtual prevents it from being inlined in the code above. Now, I was thinking about a way to get around this and thought of the following solution, where I pass a pointer to the derived class member function as a template parameter:
class BaseClass{
...
// The function is now templated by the derived function:
template<typename D, int (D::*low_level)(int)>
void myExpensiveFunction(){
for(...){
for(...){
for(...){
... = static_cast<D*>(this)->low_level(...);
...
}
}
}
}
};
class DerivedClass : public BaseClass{
// A very cheap operation that can be inlined:
inline int low_level(int i){
return a[i];
}
// Calling the base class function
void test(){
myExpensiveFunction<DerivedClass,&DerivedClass::low_level>();
}
};
Does this strategy make sense? I imagine that the low level operation will be inlined when the expensive base class function is expanded in the derived class.
I tested implementing it and it compiles and works, but I haven’t seen any noticeable differences in performance.
Kind regards,
Joel
Passing the function you want to call using a pointer to member to a base class doesn’t really improve over using a virtual function. In fact, I would expect it to make the situation worse. An alternative approach is to use a function object with an
inlinefunction call operator and call this. The “normal” approach is to kind of invert the class hierarchy and use the Curiously Recurring Template Pattern: the idea is to create a template which will derive from its template argument. The template argument is expected to provide the customization points, e.g. the functionlow_level.