[cfe-dev] problem with std::function and virtual destructor

Howard Hinnant hhinnant at apple.com
Sun Jul 15 16:40:06 PDT 2012


On Jul 15, 2012, at 3:50 PM, Howard Hinnant <hhinnant at apple.com> wrote:

> On Jul 15, 2012, at 3:59 AM, Richard Smith <richard at metafoo.co.uk> wrote:
> 
>> I'm not sure why instantiating std::function<void(X&)>::operator= would require X to be complete -- libstdc++ does not behave that way. It's possible this is a bug in libc++, but it's also possible that the standard allows this behavior and libstdc++ is just more permissive. Hopefully Howard can help with that part.
> 
> Just fyi, I'm not ignoring this question.  I just haven't figured out yet why I'm requiring complete types.  Working...

This is arguably a bug in libc++, but I need some more time before I'm ready to fix it.

libc++ has a conforming extension for std::function to check and see if the F in:


   template<class F> function& operator=(F&&);

is callable.  The standard requires that F be callable for argument types ArgTypes and return type R, but the onus is on the client to meet that requirement.  Failure to do so results in undefined behavior.  libc++'s extension is to enforce that requirement and thus emit a diagnostic if the client fails to meet it:

    template<class _Fp>
      typename enable_if
      <
        __callable<typename decay<_Fp>::type>::value,
        function&
      >::type
      operator=(_Fp&&);

__callable builds on __invokable and __invoke_of, which are implementation details originally invented for other parts of libc++, and only applied to std::function relatively recently.

When these traits were first invented, they were used in async and bind.  In this bug:

http://llvm.org/bugs/show_bug.cgi?id=9975

Richard helped me diagnose this invalid program:

#include <functional>
//#include <string>

using namespace std::placeholders;

template <typename Functor>
static void doIt(Functor f)
{
  f("");
}

void method(const std::string&) {}

int main()
{
  doIt(std::bind(&method, _1));
}

(I forgot to #include <string>).  The error message was so terrible that it took Richard's expertise to debug this.  At that time I decided to add "complete type" checks to __invokable, which greatly increased the quality of the error message of the above ill-formed program.  Now even a mortal such as myself could debug it.

Subsequently, and much later, the std::function's __callable inherited those complete type checks.

If we really need to relax the complete-type-check for function, I could split the __invokable trait:  have one with complete type checks for bind, and one without for function.  But for tonight, this explanation of what has happened is as far as I'm getting.

Comments/suggestions most welcome.

Howard




More information about the cfe-dev mailing list