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

Richard Smith richard at metafoo.co.uk
Sun Jul 15 00:59:29 PDT 2012


On Sat, Jul 14, 2012 at 11:40 PM, Rich E <reakinator at gmail.com> wrote:

> Hi all,
>
> Can anyone explain why the following doesn't compile?
>
> #include <functional>
>
> struct X{
> typedef std::function<void(X&)> callback_type;
>  virtual ~X() {}
> private:
> callback_type _cb;
> };
>
> int main(){}
>
>
> If you comment out the virtual destructor, the code compiles fine.  Full
> error report:
>
>
> ################################################
>
> CompileC
> build/test_function_callback.build/Debug/test_function_callback.build/Objects-normal/x86_64/main.o
> test_function_callback/main.cpp normal x86_64 c++
> com.apple.compilers.llvm.clang.1_0.compiler
>
>     cd /Volumes/ssd/code/sandbox/test_function_callback
>
>     setenv LANG en_US.US-ASCII
>
>
> /Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
> -x c++ -arch x86_64 -fmessage-length=0 -std=gnu++11 -stdlib=libc++
> -Wno-trigraphs -fpascal-strings -O0 -Wno-missing-field-initializers
> -Wno-missing-prototypes -Wreturn-type -Wno-non-virtual-dtor
> -Wno-overloaded-virtual -Wno-exit-time-destructors -Wformat
> -Wno-missing-braces -Wparentheses -Wswitch -Wno-unused-function
> -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value
> -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants
> -Wno-conversion -Wno-sign-compare -Wshorten-64-to-32 -Wno-newline-eof
> -Wc++11-extensions -DDEBUG=1 -isysroot
> /Applications/Xcode45-DP2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk
> -fasm-blocks -Wdeprecated-declarations -Winvalid-offsetof
> -mmacosx-version-min=10.7 -g -fvisibility-inlines-hidden
> -Wno-sign-conversion -iquote
> /Volumes/ssd/code/sandbox/test_function_callback/build/test_function_callback.build/Debug/test_function_callback.build/test_function_callback-generated-files.hmap
> -I/Volumes/ssd/code/sandbox/test_function_callback/build/test_function_callback.build/Debug/test_function_callback.build/test_function_callback-own-target-headers.hmap
> -I/Volumes/ssd/code/sandbox/test_function_callback/build/test_function_callback.build/Debug/test_function_callback.build/test_function_callback-all-target-headers.hmap
> -iquote
> /Volumes/ssd/code/sandbox/test_function_callback/build/test_function_callback.build/Debug/test_function_callback.build/test_function_callback-project-headers.hmap
> -I/Volumes/ssd/code/sandbox/test_function_callback/build/Debug/include
> -I/Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
> -I/Volumes/ssd/code/sandbox/test_function_callback/build/test_function_callback.build/Debug/test_function_callback.build/DerivedSources/x86_64
> -I/Volumes/ssd/code/sandbox/test_function_callback/build/test_function_callback.build/Debug/test_function_callback.build/DerivedSources
> -F/Volumes/ssd/code/sandbox/test_function_callback/build/Debug -MMD -MT
> dependencies -MF
> /Volumes/ssd/code/sandbox/test_function_callback/build/test_function_callback.build/Debug/test_function_callback.build/Objects-normal/x86_64/main.d
> --serialize-diagnostics
> /Volumes/ssd/code/sandbox/test_function_callback/build/test_function_callback.build/Debug/test_function_callback.build/Objects-normal/x86_64/main.dia
> -c
> /Volumes/ssd/code/sandbox/test_function_callback/test_function_callback/main.cpp
> -o
> /Volumes/ssd/code/sandbox/test_function_callback/build/test_function_callback.build/Debug/test_function_callback.build/Objects-normal/x86_64/main.o
>
>
> In file included from
> /Volumes/ssd/code/sandbox/test_function_callback/test_function_callback/main.cpp:1:
>
> In file included from
> /Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/functional:462:
>
> /Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/type_traits:2825:19:
> error: invalid application of 'sizeof' to an incomplete type 'X'
>
>     static_assert(sizeof(_Tp) > 0, "Type must be complete.");
>
>                   ^~~~~~~~~~~
>
> /Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/type_traits:2830:15:
> note: in instantiation of template class 'std::__1::__check_complete<X>'
> requested here
>
>     : private __check_complete<_Tp>
>
>               ^
>
> /Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/type_traits:2812:15:
> note: in instantiation of template class 'std::__1::__check_complete<X &>'
> requested here
>
>       private __check_complete<_T0, _Tp...>
>
>               ^
>
> /Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/type_traits:2978:15:
> note: in instantiation of template class
> 'std::__1::__check_complete<std::__1::function<void (X &)> &, X &>'
> requested here
>
>     : private __check_complete<_Fp, _Args...>
>
>               ^
>
> /Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/type_traits:2989:11:
> note: in instantiation of template class
> 'std::__1::__invokable_imp<std::__1::function<void (X &)> &, X &>'
> requested here
>
>           __invokable_imp<_Fp, _Args...>::value>
>
>           ^
>
> /Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/functional:1115:33:
> note: in instantiation of template class
> 'std::__1::__invokable<std::__1::function<void (X &)> &, X &>' requested
> here
>
>     template <class _Fp, bool = __invokable<_Fp&, _ArgTypes...>::value>
>
>                                 ^
>
> /Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/functional:1163:9:
> note: in instantiation of default argument for
> '__callable<std::__1::function<void (X &)> >' required here
>
>         __callable<typename decay<_Fp>::type>::value,
>
>         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> /Applications/Xcode45-DP2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/functional:1166:7:
> note: while substituting deduced template arguments into function template
> 'operator=' [with _Fp = const std::__1::function<void (X &)> &]
>
>       operator=(_Fp&&);
>
>       ^
>
> /Volumes/ssd/code/sandbox/test_function_callback/test_function_callback/main.cpp:3:8:
> note: definition of 'X' is not complete until the closing '}'
>
> struct X{
>
>        ^
>
> 1 error generated.
> ################################################
>
>
>
> Please let me know if I should report this as a bug.
>

What's happening is:
* We need to implicitly declare a copy assignment operator for X.
* To do that, we need to compute whether the implicitly-declared copy
assignment operator takes a const or non-const argument.
* To do that, we need to check whether any member of the class takes a
non-const argument.
* To do that, we perform overload resolution in callback_type looking for
the operator= which is used to copy _cb. And that leads to the diagnostic
you're seeing.

The C++ standard doesn't say that we should use overload resolution here,
and that we should instead just look for any operator= in the member's
class type M with a parameter type of exactly const M&, const volatile M&,
or M. So that seems like a Clang bug (please do file a bug for this).

As it happens, we'd currently have to perform overload resolution to look
up the member's operator= anyway, since it's needed to determine X's
operator='s exception specification, but the standard is being changed so
that the exception specification is computed on demand (this is core issue
1330, and we do not yet implement this part of it).

As an optimization, we defer adding the implicit members to a class if it
has no virtual functions (if there are virtual functions, the
implicitly-declared member might override a virtual function in a base
class, so we don't perform this optimization in that case). This process
isn't supposed to be able to fail, so that's safe. And that's why removing
the virtual destructor makes the bug disappear.


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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20120715/66176ad8/attachment.html>


More information about the cfe-dev mailing list