std::forward to forward a funciton

127 views Asked by At

I have the following code that just doesn't compile, particularly after it is being forwarded through std::forward

struct TestParent
{
    template< typename Fn >
    bool test( Fn&& fn )
    {
        //.. do something
        //.. check some condition
        bool someCondition = true;
        if ( someCondition )

        {
            //this call works!
            return fn();
        }

        return testAtNextLevel( std::forward< Fn >( fn ) );
    }

    template < typename Fn >
    bool testAtNextLevel( Fn&& fn )
    {
        if ( (this->*fn() )
        {
             return true;
        }

         //... test some more
         return true;
     }
}

struct TestChild: public TestParent
{
     bool thisTestOk();
     bool testAll();
}

bool TestChild::thisTestOk()
{
     return true;
}

bool testAll()
{
    auto myFunc = std::bind( &TestChild::thisTestOk, this );
    return test( myFunc );
}

When compiling I got this error message:

error: no match for 'operator->*' (operand types are 'TestParent*' and 'std::_Bind<std::_Mem_fn<bool (TestChild::*)()>(TestChild*)>')
 if ( (this->*fn)() )

Anyone has any ideas as to why after going through std::forward, the function just cannot be called? At the base class, right before the call 'testAtNextLevel', if some conditions is met we can just call the passed in function, but not after it is being forwarded to another template function?

1

There are 1 answers

1
JaMiT On BEST ANSWER

With all of those templates and auto declarations, it becomes easy to lose track of what type of data you are dealing with. Let's start near the bottom of your code:

auto myFunc = std::bind( &TestChild::thisTestOk, this );

What is myFunc? While the return type of std::bind is officially unspecified, its use is specified (see, for example, cppreference.com). Invoking this returned value as a function is equivalent to invoking thisTestOk() with its lone argument bound to this.

That is, the hidden pointer-to-TestChild argument (present in all of TestChild's non-static member functions) has been replaced by this, which has the effect of converting a member function to a non-member function. Now let's look at how you invoke this wrapper non-member function.

Within test(), this wrapper is invoked via return fn(). It is invoked as a function, and works as intended.

Within testAtNextLevel(), this wrapper is invoked via this->*fn(). This wrapper non-member function is invoked as a pointer-to-member-function, which is an error. To make it work syntactically, the invocation should be simply fn(), as it was in test(). If you really want to override the bound object and use this as the hidden argument to fn(), you need to pass something different as the argument to testAtNextLevel(), probably a pointer-to-member (and it would have to be a pointer-to-TestParent-member, not a pointer-to-TestChild-member).