Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

functor

Header <boost/core/functor.hpp>

Authors

  • Andrey Semashev
[Note] 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
  • Effects: return Function(std::forward< Args >(args)...).
  • Throws: Nothing, unless invoking Function throws.
  • Note: This function only participates in overload resolution if Function(std::forward< Args >(args)...) is a valid call expression.

PrevUpHomeNext