Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

how to effectively turn rvalue reference to lvalue in perfect forwarding

I am implementing a simple generic functor class which can store a function as well as its arguments, and thus it can be called later. The function can be a free function or member function of a class, which takes arbitrary number of arguments. So I use perfect forwording.

Here is the class:

template< typename FUNC, typename...ARGS>
class GenericFunctor{

    using ReturnType = std::invoke_result_t<FUNC, ARGS...>;

    std::function< ReturnType() > functor;

public:

    GenericFunctor( FUNC&& func, ARGS &&... args ){
        functor = std::bind( std::forward<FUNC>(func), std::forward<ARGS>(args)... );
    }

    void run(){
        functor();
    }

};

I test it with this simple class:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

class Person{

    int age;
    double height;

public:

    Person( int age, double height):age(age), height( height){}

    void print(){
        cout << age << " " << height << "\n";
    }

    int getAge(){
        return age;
    }
};

I expect the following 3 case can work:

GenericFunctor f_1( &Person::getAge, std::ref(p1) ) );
f_1.run();

GenericFunctor f_2( &Person::getAge, std::move(p1) );
f_2.run();

GenericFunctor f_3( &Person::getAge, p1 );
f_3.run(); 

But in the case of f_3, I got this error:

error: cannot bind rvalue reference of type 'Person&&' to lvalue of type 'Person'

I know what the above message means, but I have no idea how to modify my class, GenericFunctor, to make it generic enough so that all of the above 3 cases can work just as std::thread. Could I have some guidance of how to modify my GenericFunctor?

>Solution :

CTAD (class template argument deduction) doesn’t respect forwarding references (treats them as rvalue references, only accepting rvalues), unless you add a custom deduction guide.

Add one after your class:

template <typename FUNC, typename ...ARGS>
GenericFunctor(FUNC &&, ARGS &&...) -> GenericFunctor<FUNC, ARGS...>;

But the whole class doesn’t really need to have FUNC and ARGS... as template parameters. Only the constructor needs them.

The whole class only needs the return type. You can write a deduction guide to avoid having to write it manually:

template <typename R>
class GenericFunctor
{
    std::function<R()> functor;

    // ...

    template <typename FUNC, typename ...ARGS>
    GenericFunctor(FUNC &&func, ARGS &&... args) {/*...*/}
};

template <typename FUNC, typename ...ARGS>
GenericFunctor(FUNC &&, ARGS &&...) -> GenericFunctor<std::invoke_result_t<FUNC &&, ARGS &&...>>;
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading