Bit of a mess this answer, but a few things to think about....
A virtual function - used as a callback - is more inherently restrictive:
you know a derived classes must specify an implementation (given your = 0
), but it won't vary during the derived object's lifetime (something similar may be done with const std::function
objects set in the initialiser list, but need not be),
you know the callbacks are active from the time the constructor completes until destruction
By way of contrast, someone coming to grips with code that specifies a set of callback lambdas has to look more carefully at how and when they're set, and whether they may be varied during the lifetime of the object. Lambdas may be capturing more inputs and context from the function they're created in - that adds to the potential complexity.
Setting std::function
callbacks may require more explicit procedural code to set the std::function
variables, unless that's done exclusively in the constructor's initialisation list. virtual function override
s are more declarative, which some people prefer.
The most-derived implementation of a virtual function is used, while specifying a lambda to a std::function
in the most-derived constructor may require explicitly passing it to base class constructors at all levels through the hierarchy.
The const
-ness of virtual functions works intuitively, but derived classes specifying behaviour by setting std::function
members to lambdas have opportunity to grab non-const
access to the derived object, regardless of what the base class design suggests those callbacks should need.
virtual dispatch is understood intimately by the compiler, and often resolved - with the function potentially inlined - when the dynamic type of the object is known at compile time. It's a stretch to think equivalent optimisation would necessarily be achieved for std::function
objects where the lambdas are nominally set by derived functions at runtime....
Virtual functions are efficiently factored as per-object pointers to per-class tables, which would be more trouble to orchestrate manually using std::function
s. The easy approach of a std::function
member per callback is less memory efficient.