I noticed some strange results with std::is_convertible and std::is_assignable when std::function object and std::bind are involved.
I would assume that when these functions return true, the conversion can be made. Or am I missing something?
The following code prints different results on different compilers, and I would expect it to print 0 since these types cannot be assigned.
#include <type_traits>
#include <functional>
#include <iostream>
int main()
{
std::cout << std::is_convertible<std::function<void(int)>, std::function<void()>>::value << std::endl;
}
It prints 0 on the following compilers:
It prints 1 on the following compilers:
Is there any correct answer? Are these bugs in compilers or am I messing with stuff that is compiler specific?
In C++11, std::function
's constructor taking an arbitrary functor type is specified as (quoting N3337 §20.8.11.2.1 [func.wrap.func.con]/p7):
template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires:
F
shall beCopyConstructible
.f
shall beCallable
(20.8.11.2) for argument typesArgTypes
and return typeR
. The copy constructor and destructor ofA
shall not throw exceptions.
Violation of a Requires clause (passing an f
not Callable
for argument types ArgTypes
and return type R
) is undefined behavior, so the library is free to do whatever it wants in that case. The library may take the constructor out of overload resolution, but it doesn't have to, and if it doesn't, then you will have issues with overload resolution and std::is_convertible
- it will report that pretty much everything under the sun is convertible to std::function
(including stuff like double
!).
Hence, in LWG issue 2132, the standard was modified to require implementations to remove these constructors from overload resolution (via SFINAE or a similar technique) if the functor isn't Callable
for the specified argument types and return type. It now reads:
template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires:
F
shall beCopyConstructible
.8 Remarks: These constructors shall not participate in overload resolution unless
f
isCallable
(20.9.11.2) for argument typesArgTypes...
and return typeR
.
So if your standard library implements this resolution, then std::is_convertible<std::function<void(int)>, std::function<void()>>::value
is false
. Otherwise, it's implementation-dependent.
I would assume that when these functions return true, the conversion can be made. Or am I missing something?
Type traits such as std::is_convertible
, std::is_constructible
, or std::is_assignable
only considers the immediate context - i.e., whether there's a matching function signature that's accessible and not deleted. They don't check if the function's body would compile when instantiated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With