...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Note | |
---|---|
This component requires a compiler supporting C++17 or newer. |
The header <boost/core/functor.hpp>
defines the boost::core::functor
class template that wraps a raw
function specified in its template parameter into a function object class.
The function object forwards any arguments passed to it to the wrapped function
and returns the result of the call.
The functor
wrapper can be
useful in cases when a function object class type is required, for example,
for use with smart pointers such as std::unique_ptr
,
where the actual logic of the function object is already implemented as a
raw function, possibly provided by a third party library. Since functor
is default-constructible and does
not store a pointer to the wrapped function internally, using functor
is less error-prone and more efficient
than using the pointer to function instead. For example, with std::unique_ptr
you don't need to pass a pointer to the deleter function in the std::unique_ptr
constructor, and the std::unique_ptr
object does not store and invoke a pointer to the deleter function. With
functor
, the deleter function
becomes part of the std::unique_ptr
type, which prevents mixing
pointers with incompatible deleters.
void my_deleter(void* p); using malloc_ptr = std::unique_ptr< char, boost::core::functor< std::free > >; using my_ptr = std::unique_ptr< char, boost::core::functor< my_deleter > >; my_ptr create_string(std::size_t size); void consume_string(my_ptr&& str); malloc_ptr ptr1(static_cast< char* >(std::malloc(size))); // ptr1 = allocate_string(size); // error, cannot convert my_ptr to malloc_ptr my_ptr ptr2 = create_string(size); // ok // consume_string(std::move(ptr1)); // error, cannot convert malloc_ptr&& to my_ptr consume_string(std::move(ptr2)); // ok
Using functor
may also be
beneficial for reducing generated code sizes. For example, in order to avoid
storing and invoking a pointer to the deleter function in std::shared_ptr
,
one may be inclined to use lambda functions to wrap the deleter function
call like this:
std::shared_ptr< int > ptr(static_cast< int* >(std::malloc(sizeof(int))), [](int* p) { std::free(p); });
The problem is that every lambda function declaration introduces a unique
type, even if the lambda function definition matches exactly one of the previously
declared lambda functions. Thus, if std::shared_ptr
objects like the one above are created in multiple places in the program,
the definition of the shared pointer counter and associated code and data
(e.g. virtual function table) will be duplicated for each instance.
Replacing the lambda function with functor
solves this problem without sacrificing readability or efficiency:
std::shared_ptr< int > ptr(static_cast< int* >(std::malloc(sizeof(int))), boost::core::functor< std::free >());
namespace boost::core { template< auto Function > struct functor { template< typename... Args > decltype(auto) operator() (Args&&... args) const noexcept(...); }; } // namespace boost::core
return
Function(std::forward<
Args >(args)...)
.
Function
throws.
Function(std::forward< Args
>(args)...)
is a valid call expression.