简体   繁体   中英

How to overload on std::function signature in C++

I know this question has been asked before, but despite being a fairly experienced coder I don't understand the answers and see no way to respond to these previous questions to ask for clarification. There is no "reply" link or anything. Besides, those questions were quite old. So, I'm asking the question afresh.

I have a class, in which I am overloading the += operator. I want one overload to take a bare function pointer, and the other to take a std::function:

void operator+=(void (*handler)());
void operator+=(function<void (void *, T)> handler);

Usage:

MyClass a;
a += [](){ DoSomething(); };
a += [](void *x, T y){ DoSomething(); };

Unfortunately, the code does not compile because the compiler cannot determine that the second overload is unsuitable for the first += call.

How do I define my operator+= member functions to correct this problem? I don't want to change how the operators are used (eg by using an explicit cast). I want them to work as demonstrated above.

In addition, it would be handy to have the following overload as well:

void operator+=(function<void()> handler);

But again, I can't overload based on the signature of the function<> template.

See this thread for further examples: Isn't the template argument (the signature) of std::function part of its type? (I tried implementing the various solutions mentioned in that thread, and none of them would compile )

I've been programming for many years in a number of languages, but my C++ skills are a little rusty.

Following code works well with g++ 4.7.2:

#include <functional>
#include <iostream>

template <typename T>
typename std::enable_if<std::is_convertible<T, void(*)()>::value>::type
foo(T&&)
{
    std::cout << "foo(void(*)())" << std::endl;
}

void foo(std::function<void(void*,int)>)
{
    std::cout << "foo(std::function<void(void*,int)>)" << std::endl;
}

int main()
{
    foo([]{});
    foo([](void*,int){});
}

The problem is caused by the constructor of std::function , which is declared as template <class F> function(F); . That constructor accepts everything as its argument. If that argument doesn't have an appropriate operator() , an error is generated during the instantiation of the constructor.

Unfortunately the overload resolution process doesn't require any template functions to be instantiated, so the compiler believes everything can be converted to any std::function type.

Ok, if you have an std::vector<std::function<void()>> you even don't need to have a bunch of overloads. The simplest way is to define a templated operator+= , possible "guarded" with std::enable_if to be turned ON only for callable object or function pointers. So passing lambdas or raw pointers will do automatic conversion for you on assign (push_back/emplace_back). Passing std::function will just assign as expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM