1

As per the documentation(http://www.cplusplus.com/reference/functional/function/), which says that:

Class that can wrap any kind of callable element (such as functions and function objects) into a copyable object, and whose type depends solely on its call signature (and not on the callable element type itself).

How to comprehend that the type of std::function depends solely on its call signature(and not on the callable element type itself)? Could somebody make it clear by giving some simple examples?I would be greatful to have some help with this question.

sunshilong369
  • 646
  • 1
  • 5
  • 17

5 Answers5

3

The template type effectively denotes a function signature (excluding name of course).

std::function<bool(Bar const&, Foo const&)>

Can hold a functor, member function pointer, function pointer or lambda. But the callable must have the bool (Bar const&, Foo const&) signature.

class Foo {};
class Bar {};
class FunctorEx
{
  public:
    bool operator()(Bar const&, Foo const&)
    {
        return true;
    }
} FunctorExInst;

class MemFunction
{
  public:
    bool MemFunctionEx(Bar const&, Foo const&)
    {
        return true;
    }
} MemFunctionInst;

bool FunctionEx(Bar const&, Foo const&)
{
    return true;
}

int main()
{
    auto LambdaEx = [] (Bar const&, Foo const&) -> bool
    {
        return true;
    };

    std::function<bool(Bar const&, Foo const&)> exFunctionVar;
    exFunctionVar = std::bind(&MemFunction::MemFunctionEx, &MemFunctionInst, std::placeholders::_1, std::placeholders::_2);
    exFunctionVar = FunctorExInst;
    exFunctionVar = FunctionEx;
    exFunctionVar = LambdaEx;
}

Despite MemFunctionInst, FunctorExInst, FunctionEx, LambdaEx all being of different types, they can all be assigned to the same std::function variable due to a technique called type erasure.

George
  • 2,101
  • 1
  • 13
  • 27
2

There are different kinds of callable's, namely functions and functors. std::function was designed to not mater which of those you give to it, for example, lets consider a std::function <void()>. Here we say the function is one that return nothing and takes nothing, which means that

void foo() {}
auto bar = []{};
struct { void operator()(){} } baz;

are all things that can be assigned to it, even though they are all different types.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
1

These two "callable elements" have the same call signatures (namely, int(float)), even though they have different types:

struct Functor {
  int operator()(float arg) {
    ...
  }
};

int function(float arg) {
  ...
}

This means you can use the same std::function type to represent either of them:

std::function<int(float)> f;
f = Functor();
f = function;
Thomas
  • 174,939
  • 50
  • 355
  • 478
1

If we forget all gory details, then the important method of std::function is operator().

Consider you write a wrapper for callables with signature double (int,int), then the wrapper will be something similar to:

struct example_wrapper {
     double operator()(int a,int b) { return 42; }
};

The details I left out is what enables you to make std::function wrap any kind of callable. If the callables have the same signature, then the wrapper can have the exact same public interface (ie it is the same type). If the callables have different signature, the wrapper needs a different public interface (ie it is a different type).

cigien
  • 57,834
  • 11
  • 73
  • 112
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Thank you.If i understand you correctly, i think the `std::function` may be implemented like this.Am i right? – sunshilong369 Jun 02 '20 at 15:02
  • @sunshilong369 not sure what you mean. Actually I left out all implementation and only concentrated on the part that explains why same signature is same `std::function` type. – 463035818_is_not_an_ai Jun 02 '20 at 18:28
1

This means simply that the type of the callable stored in std::function does not impact the type of std::function itself, only the function signature does.

This can for example be implemented as such non-optimized:

template<class F, class Ret, class... Args>
Ret invoke_functor(void* func, Args&& ...args)
{
    return (*reinterpret_cast<F*>(f))(std::forward<Args>(args)...);
}

template<class Ret, class... Args>
Ret invoke_function(void* func, Args&& ...args)
{
    return reinterpret_cast<Ret(*)(Args...)>(func)(std::forward<Args>(args...);
}

template<class Ret, class... Args> //wrong but close enough
class function
{
public:
    template<class Functor>
    function(Functor&& f) :
        m_func(new Functor(f)), //leak
        m_invoke(&invoke_functor<Functor, Ret, Args...>)
    { }

    function(Ret(*ptr)(Args...)) :
         m_func(ptr),
         m_invoke(&invoke_function<Ret, Args...>)
    { }

     Ret
     operator()(const Args& ...args)
     {
          return m_invoke(m_func, args);
     }

private:
    void* m_func;
    Ret(*m_invoke)(void*, Args...);
}

Here std::function can be created from both functions and callable structures as shown by the other answers.

Mestkon
  • 3,532
  • 7
  • 18