I'm learning to implement std::function
and have found several articles about this. Unfortunately, none of their implementations can report a mistake when I assign a funtor with different signature. However, the STL version can alert me as I expect. Obviously, this kind of feature is useful to check such stupid mistake beforehand.
I read the source of MSVC implementation, but can not understand what happened within the template argument _Enable_If_callable_t
of function& operator=(_Fx&& _Func)
of std::function
. Seems like this SFINAE trick prohibits such wrong assignment operator existing.
Please tell me the theory behind this and if I can implement such feature in my handmaking version.
CodePudding user response:
Cppreference says about operator=
:
Sets the target of *this to the callable f, as if by executing function(std::forward(f)).swap(*this);. This operator does not participate in overload resolution unless f is Callable for argument types Args... and return type R.
This can be easily checked with std::is_invocable_r
helper:
#include <type_traits>
template <class>
class function {};
template <class R, class... Args>
class function<R(Args...)> {
public:
template <typename F, typename x = std::enable_if_t<
std::is_invocable_r<R, F, Args...>::value>>
function& operator=(F&& f) {
// logic...
return *this;
}
};
#include <functional>
#include <string>
void bar1(std::string) {}
void bar2(int) {}
void bar3(float) {}
int bar4(int) { return 0; }
int main(int argc, char** argv) {
//std::function<void(int)> f;
function<void(int)> f;
// f = bar1; ERROR
f = bar2;
f = bar3;
f = bar4;
return 0;
}
I am use the SFINAE version with an extra template argument as I find it most clear but return-value-based can be used too.
Same as for std::function
, this is really forgiving - extra return values in case of R=void
are ignored, implicit conversions are enabled. This matches what std::is_invocable_r
allows.