3
votes

Consider the following code which works as expected:

#include <iostream>
#include <functional>

struct foo
{
    std::function<int()> get;
};

struct bar
{
    int get()
    {
        return 42;
    }
};

int main()
{
    foo f;
    bar b;
    f.get = std::bind(&bar::get, &b);

    if (f.get())
        std::cout << "f.get(): " << f.get() << std::endl;

    return 0;
}

Now, let's assume that bar::get() is a const member function:

#include <iostream>
#include <functional>

struct foo
{
    std::function<int()const> get;
};

struct bar
{
    int get() const
    {
        return 42;
    }
};

int main()
{
    foo f;
    bar b;
    f.get = std::bind(&bar::get, &b);

    if (f.get())
        std::cout << "f.get(): " << f.get() << std::endl;
}

Using GCC 9.2, this snipped throws the following compiler error:

main.cpp:6:31: error: field 'get' has incomplete type 'std::function<int() const>'
    6 |     std::function<int()const> get;
      |                               ^~~
In file included from /usr/local/include/c++/9.2.0/functional:59,
                 from main.cpp:2:
/usr/local/include/c++/9.2.0/bits/std_function.h:128:11: note: declaration of 'class std::function<int() const>'
  128 |     class function;
      |           ^~~~~~~~

I fail to understand why foo::get has incomplete type. Could somebody point me towards the right direction for understanding this behavior and "fixing" it accordingly? I have the need to bind a const member function to a function pointer.

4
So you want const std::function<int()> get;? Or do you want int (bar::*get)() const;? The constness is checked at std::bind, not at std::function.KamilCuk

4 Answers

4
votes

int()const is an abominable type.

std::function<int()const> is not a type, because it doesn't match the only defined specialisation

namespace std {
    template< class R, class... Args >
    class function<R(Args...)> { ... };
}
2
votes

Just use std::function<int()>.

The const bit only makes sense for member functions. You've already bound bar::get to an instance b to save it as a std::function.

1
votes

As mentioned by @KamilCuk:

The constness is checked at std::bind, not at std::function

You don't need to pass explicit const to std::function. Just use your older prototype: std::function<int()>. It will work if you don't have const overload (that mean, you have either one of these int bar::get() or int bar::get() const) for the same member function (Otherwise, you need to type cast explicity).

Actually, your function (int bar::get() const) will be having a signature like this (behind the scenes):

// int bar::get() const
int get(const bar *const this)
{
    return 42;
}

// int bar::get() 
int get(bar *const this)
{
    return 42;
}

If you have overloads and want to bind specific member function, you can do something like this:

typedef int(bar::*fptr)(void) const; // or remove const
std::bind((fptr)&bar::get, &b );

See this:

#include <iostream>
#include <functional>
#include <vector>

struct foo
{
    std::function<int()> get;
};

struct bar
{
    int get()
    {
        return 42;
    }

    int get() const
    {
        return 50;
    }
};

int main()
{
    foo f;
    bar b;
    typedef int (bar::*fptr)(void);
    typedef int (bar::*fcptr)(void) const;
    f.get = std::bind((fptr)&bar::get, &b);

    if (f.get())
        std::cout << "f.get(): " << f.get() << std::endl;

    f.get = std::bind((fcptr)&bar::get, &b);

    if (f.get())
        std::cout << "f.get(): " << f.get() << std::endl;
}

Output:

f.get(): 42
f.get(): 50
1
votes

std::bind does not pass through constness to the callable. Therefore, the following would work:

struct foo {
    std::function<int()> get;
    //                 ^ note there is no 'const'
};