Home > Net >  How to implement a std::function with operator= that can check if its rhs has same signature
How to implement a std::function with operator= that can check if its rhs has same signature

Time:08-11

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.

  • Related