c++c++23boost-beast

deducing this and std::bind


I'm getting errors when trying to pass a function using deducing-this to std::bind. See the example below.

#include <iostream>
#include <functional>

struct A {
    void foo_deducing_this(this auto& self, int x) {
        self.bar(x);
    }
};

template <typename Deriv>
struct B {
    void foo_crtp(int x) {
        static_cast<Deriv *>(this)->bar(x);
    }
};

struct C : A, B<C> {
    void bar(int x) {
        std::cout << "number is " << x << "\n";
    }
};

void test(const std::function<void(int)>& f, int x) {
    f(x);
}

int main() {
    C c;

    c.foo_deducing_this(1);
    c.foo_crtp(2);
    c.bar(3);

    // this doesn't compile
    // test(std::bind_front(&C::foo_deducing_this, c), 5);
    
    test(std::bind_front(&C::foo_crtp, c), 6);
    test(std::bind_front(&C::bar, c), 7);
}

I'd like to use deducing this to avoid some templates required by CRTP but still taking advantage of the same pattern. Using boost beast the pattern seems to be to pass callbacks around with

  stream.async_read(buffer,
                    beast::bind_front_handler(&Connection::on_read,
                                               this->shared_from_this()));

So my Connection callbacks need to support being "binded" to and that seems to be an issue when using deducing this but I can't figure out why.

EDIT: This was tested on clang 18.1.0. Godbolt link.


Solution

  • "Deducing this", as you are using it, makes the member-function a templated function, due to you using "auto" as the parameter - auto here has the same effect as declaring the parameter via template<>. This means that it's no longer clear which variant of foo_deducing_this would be requested when trying to get a function-ptr. You can see that it's not related at all to std::bind (and get a more sensible error), if you simply put this line in your code:

    &C::foo_deducing_this;
    

    This results in clang emitting the following error:

    <source>:34:5: error: reference to overloaded function could not be resolved; did you mean to call it?
       34 |     &C::foo_deducing_this;
          |     ^~~~~~~~~~~~~~~~~~~~~
    

    You can specify the correct overload by casting the type of member-function ptr in the call to std::bind_front (which must include the expected const-ness):

    using MemberFuncPtr = void (*)(A&, int);
    
    std::bind_front((MemberFuncPtr)&A::foo_deducing_this);
    

    or even simpler, specify the template parameter:

    std::bind_front(&A::foo_deducing_this<A&>);
    

    Interestingly (which is why I left the casting-example in), it seems that this function is now no longer even a true member-function. Didn't even know that, but I have not much experience with deducing this.