Passing function as universal reference? Why?



  • Why Functor&& f is possed as universal ref? this is the function, when the function can become a rvalue?

    /**
           *  Calls function <code>f</code> in this thread and returns a future<T> that can
           *  be used to wait on the result.  
           *
           *  @param f the operation to perform
           *  @param prio the priority relative to other tasks
           */
          template<typename Functor>
          auto async( Functor&& f, const char* desc FC_TASK_NAME_DEFAULT_ARG, priority prio = priority()) -> fc::future<decltype(f())> {
             typedef decltype(f()) Result;
             typedef typename fc::deduce<Functor>::type FunctorType;
             fc::task<Result,sizeof(FunctorType)>* tsk = 
                  new fc::task<Result,sizeof(FunctorType)>( fc::forward<Functor>(f), desc );
             fc::future<Result> r(fc::shared_ptr< fc::promise<Result> >(tsk,true) );
             async_task(tsk,prio);
             return r;
          }
    


  • Take this functor:

    class ExampleFunctor
    {
        int* m_longArray;
        int m_longArraySize;
    public:
        ExampleFunctor(int sz = 10000)
            :m_longArraySize(sz)
        {
            if (m_longArraySize < 10000) m_longArraySize = 10000;
            m_longArray = new int[m_longArraySize];
            for (int i = 0; i < m_longArraySize; ++i)
                m_longArray[i] = rand();
        }
        ~ExampleFunctor() { if (m_longArray) delete[] m_longArray; }
        ExampleFunctor(const ExampleFunctor& other)
            :m_longArraySize(other.m_longArraySize)
            , m_longArray(new int[other.m_longArraySize])
        {
            for (int i = 0; i < m_longArraySize; ++i)
                m_longArray[i] = other.m_longArray[i];
        }
        ExampleFunctor(ExampleFunctor&& other)
            :m_longArraySize(other.m_longArraySize)
            , m_longArray(other.m_longArray)
        {
            other.m_longArray = nullptr;
        }
        ExampleFunctor& operator=(ExampleFunctor&& other)
        {
            m_longArraySize = other.m_longArraySize;
            std::swap(other.m_longArray, m_longArray);
            return *this;
        }
        ExampleFunctor& operator=(const ExampleFunctor& other)
        {
            if (m_longArray) delete[] m_longArray;
            m_longArraySize = other.m_longArraySize;
            m_longArray = new int[m_longArraySize];
            for (int i = 0; i < m_longArraySize; ++i)
                m_longArray[i] = other.m_longArray[i];
            return *this;
        }
        int operator()()
        {
            int result = 0;
            for (int i = 0; i < m_longArraySize - 1; ++i)
                result += (m_longArray[i] ^= m_longArray[i + 1]);
            return result;
        }
        static ExampleFunctor createFunctor(int size) { return ExampleFunctor(size); }
    };
    

    Pass it to your template async(ExampleFunctor::createFunctor(1000000));

    Let's see what happens in each case:

    • auto async(Functor f : takes a deep copy of the functor
    • auto async(const Functor& f : if you want to call the functor (since it's not const) you'll have to take a deep copy of it sooner or later
    • auto async(Functor& f: this would work in the example above but if operator()() was const and you create it with const ExampleFunctor temp(1000000); then you can't pass it anymore
    • auto async(Functor&& f: accepts all kind of arguments without compromising efficiency


  • BTW, keep in mind that there is no such thing as "universal ref" in C++ standard. It's just a way of thinking about rvalue ref T&& in contexts which provide type deduction for T


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.