c++c++11castingstd-function

Implicit conversion of an std::function<void()> to void*


Considering the following code (see https://rextester.com/OAWUM62639):

#include <iostream>
#include <functional>

void foo( int x_ )
{
    std::cout<< "Value: " << x_ << "\n";
}

void something( void *c )
{
    auto *f = static_cast<std::function<void()>*>(c);
    (*f)();
}

int main()
{
    int x = 2;
    
    std::function<void()> lmbd = [=](){ foo(x); }; // lambda with capture.
    
    something( &lmbd ); // is this implicit conversion safe?
}

Is the implicit conversion from a std::function<void()> to a void* safe?

The code seems to be running as expected, but I am not sure about the underlying rules that the C++11 standard imposes in such situations.

A similar question is this one here: How do I pass a std::function object to a function taking a function pointer?, but the accepted answer suggests using a reinterpret_cast. Is the reinterpret_cast a necessity in this case (see https://rextester.com/ZUY69757)?


Solution

  • Is the implicit conversion from a std::function<void()> to a void* safe?

    All of the shown code is safe.

    In general, for any object of type T it is completely fine to convert a pointer to it to void* and then static_cast it back to T* to then use it.

    The tricky bits about such an interface are:

    1. Casting to void* loses type information, but you must cast back to the exact original type to use the pointed-to object. Casting to any other type will likely end in undefined behavior. So, usually the only reasonable behavior for such an interface is to only copy the pointer value itself and pass the pointer back to context provided by the user, which knows the actual type.

    2. You lose any ownership semantics. The void* pointer is always purely observing. It doesn't carry any ownership. Therefore the caller has to make sure that the actual object (i.e. lmbd) outlives any use of the void* pointer by something. If something stores the pointer somewhere for e.g. a later callback, that can become tricky.

    These kind of interfaces are sometimes necessary when interacting with C, but for pure C++ code there is no need to do it this way. Templates or passing std::function directly are superior solutions.