Given:
#include <functional>
class world_building_gun;
class tile_bounding_box;
typedef std::function<void (world_building_gun, tile_bounding_box)> worldgen_function_t;
void foo() {
worldgen_function_t v;
worldgen_function_t w(v);
}
Should this compile? My compilers say:
Yes: GCC/stdlibc++ (also boost::function is yes in both GCC and Clang)
No: Clang/libc++ ( http://libcxx.llvm.org/ , Clang 3.0, libc++ SVN as of today)
(If "no" is the correct answer, I will fix my real code to put complete types in more headers or use boost::function.)
EDIT: Here is the Clang error message:
In file included from foo.cpp:2:
In file included from /usr/include/c++/v1/functional:462:
/usr/include/c++/v1/type_traits:2766:19: error: invalid appli
static_assert(sizeof(_Tp) > 0, "Type must be complete.");
^~~~~~~~~~~
/usr/include/c++/v1/type_traits:2752:15: note: in instantiation of template class 'std::__1::__check_complete<world_buildin
: private __check_complete<_Hp>,
^
/usr/include/c++/v1/type_traits:2753:15: note: in instantiation of template class 'std::__1::__check_complete<world_buildin
private __check_complete<_T0, _Tp...>
^
/usr/include/c++/v1/type_traits:2919:15: note: in instantiation of template class 'std::__1::__check_complete<std::__1::fun
world_building_gun, tile_bounding_box>' requested here
: private __check_complete<_Fp, _Args...>
^
/usr/include/c++/v1/type_traits:2930:11: note: in instantiation of template class 'std::__1::__invokable_imp<std::__1::func
world_building_gun, tile_bounding_box>' requested here
__invokable_imp<_Fp, _Args...>::value>
^
/usr/include/c++/v1/functional:1115:33: note: in instantiation of template class 'std::__1::__invokable<std::__1::function<
world_building_gun, tile_bounding_box>' requested here
template <class _Fp, bool = __invokable<_Fp&, _ArgTypes...>::value>
^
/usr/include/c++/v1/functional:1141:35: note: in instantiation of default argument for '__callable<std::__1::function<void (world_building_gun, tile_bounding_box)> >' required here
typename enable_if<__callable<_Fp>::value>::type* = 0);
^~~~~~~~~~~~~~~
/usr/include/c++/v1/functional:1140:7: note: while substituting deduced template arguments into function template 'function' [with _Fp = std::__1::function<void
(world_building_gun, tile_bounding_box)>]
function(_Fp,
^
foo.cpp:4:7: note: forward declaration of 'world_building_gun'
class world_building_gun;
^
In file included from foo.cpp:2:
In file included from /usr/include/c++/v1/functional:462:
/usr/include/c++/v1/type_traits:2766:19: error: invalid application of 'sizeof' to an incomplete type 'tile_bounding_box'
static_assert(sizeof(_Tp) > 0, "Type must be complete.");
^~~~~~~~~~~
/usr/include/c++/v1/type_traits:2753:15: note: in instantiation of template class 'std::__1::__check_complete<tile_bounding_box>' requested here
private __check_complete<_T0, _Tp...>
^
/usr/include/c++/v1/type_traits:2753:15: note: in instantiation of template class 'std::__1::__check_complete<world_building_gun, tile_bounding_box>' requested here
private __check_complete<_T0, _Tp...>
^
/usr/include/c++/v1/type_traits:2919:15: note: in instantiation of template class 'std::__1::__check_complete<std::__1::function<void (world_building_gun, tile_bounding_box)> &,
world_building_gun, tile_bounding_box>' requested here
: private __check_complete<_Fp, _Args...>
^
/usr/include/c++/v1/type_traits:2930:11: note: in instantiation of template class 'std::__1::__invokable_imp<std::__1::function<void (world_building_gun, tile_bounding_box)> &,
world_building_gun, tile_bounding_box>' requested here
__invokable_imp<_Fp, _Args...>::value>
^
/usr/include/c++/v1/functional:1115:33: note: in instantiation of template class 'std::__1::__invokable<std::__1::function<void (world_building_gun, tile_bounding_box)> &,
world_building_gun, tile_bounding_box>' requested here
template <class _Fp, bool = __invokable<_Fp&, _ArgTypes...>::value>
^
/usr/include/c++/v1/functional:1141:35: note: in instantiation of default argument for '__callable<std::__1::function<void (world_building_gun, tile_bounding_box)> >' required here
typename enable_if<__callable<_Fp>::value>::type* = 0);
^~~~~~~~~~~~~~~
/usr/include/c++/v1/functional:1140:7: note: while substituting deduced template arguments into function template 'function' [with _Fp = std::__1::function<void
(world_building_gun, tile_bounding_box)>]
function(_Fp,
^
foo.cpp:5:7: note: forward declaration of 'tile_bounding_box'
class tile_bounding_box;
^
2 errors generated.
Clang+libc++ compiles successfully if I delete the line "worldgen_function_t w(v);" or if I make the classes complete types.
Edit: Apperently, this issue is now fixed, so the below text can be seen as history. :)
The issue is indeed (as I predicted) with libc++'s SFINAE checks in the templated ctor (for a reasoning, check this question). It checks if the following (for example) is valid and gives a nice and clean error at the construction site rather than deep inside the guts of
std::function
(try the following example with libstd++ or MSVC... shudder):libc++ will cause the compiler to spit out something along the lines of "no constructor found that matches the argument list
void (*)(int*)
", since the only applicable one (the templated ctor) gets SFINAE'd out.However, so that the
__callable
and__invoke_imp
checks work, the argument and return types need to be complete, since otherwise implicit conversions wouldn't be taken into account here.The reason that the templated ctor is even looked at is that all ctors are enumerated before considering a best match (in this case the copy ctor).
Now, the standard is very clear that the argument and return types need to be complete when constructing a
std::function
object from a callable object (aka calling the templated ctor):§20.8.11.2.1 [func.wrap.func.con] p7
(Note: "Requires" addresses the user of the functionality, not the implementer.)
§20.8.11.2 [func.wrap.func] p2
§20.8.2 [func.req]
So, libc++ is certainly within its rights to do the SFINAE check in the templated ctor, since the types need to be complete, since otherwise you'd get undefined behaviour. However, it may be a bit unfortunate and be considered a defect that the safety check for a complete type triggers even if the actual SFINAE check is never needed (because the copy ctor will always be invoked). This may be alleviated by making the
callable
check a lazy one, likeThis should only trigger the
callable
SFINAE check ifF
is not actuallystd::function<...>
.Man, I may have digressed a bit here at the end...