Queue for distributing tasks between threads I implemented the following class to dispatch std::function&lt;void(void)&gt; objects to a thread pool. Multiple threads will block on the pop call until a task is available.

I am using the C++14 standard.

#pragma once

#include&lt;memory&gt;
#include&lt;queue&gt;
#include&lt;mutex&gt;
#include&lt;condition_variable&gt;

namespace alpha {

    template&lt;typename T&gt;
    class AsyncQueue {
        private:
            std::shared_ptr&lt;std::queue&lt;T&gt;&gt; queue;
            std::shared_ptr&lt;std::mutex&gt; mutex;
            std::shared_ptr&lt;std::condition_variable&gt; cond;

        public:
            /* Big 5 */
            AsyncQueue(const AsyncQueue&amp; other) = default;
            AsyncQueue(AsyncQueue&amp;&amp; other) = default;
            AsyncQueue&amp; operator=(const AsyncQueue&amp; other) = default;
            AsyncQueue&amp; operator=(AsyncQueue&amp;&amp; other) = default;
            virtual ~AsyncQueue() = default;

            /**
             * @brief Default constructor
             */
            AsyncQueue() :
                queue(new std::queue&lt;T&gt;()),
                mutex(new std::mutex),
                cond(new std::condition_variable()) {
            }

            /**
             * @brief Push a value to the async queue
             */
            void push(const T&amp; object) {
                {
                    std::lock_guard&lt;std::mutex&gt; lock(*mutex);
                    queue-&gt;push(object);
                }
                cond-&gt;notify_one();
            }

            /**
             * @brief Push a value to the async queue (move variant)
             */
            void push(T&amp;&amp; object) {
                {
                    std::lock_guard&lt;std::mutex&gt; lock(*mutex);
                    queue-&gt;push(object);
                }
                cond-&gt;notify_one();
            }

            /**
             * @brief Pop a value from the queue
             */
            T&amp;&amp; pop() {
                std::unique_lock&lt;std::mutex&gt; lock(*mutex);
                while(queue-&gt;empty()) {
                    cond-&gt;wait(lock);
                }
                T object = std::move(queue-&gt;front());
                queue-&gt;pop();
                return std::move(object);
            }
    };
}


Specific questions:


Is there a way to keep both implementations of push DRY? The only difference is the type of the argument.
Is this safe? I tested it multiple times with true; while [[ $? == 0 ]]; do ./test; done, but i'd feel a lot better if someone told me that my idea of thread-safety is correct.
pop returns a rvalue-reference, which seems to cause problems when passing it directly to a function with the argument type const T&amp;. Assigning it to a local variable first solves that, but is there a way to make push return a rvalue-reference only when it is needed? I am asking because i know C++ compilers implicitly move a lot.


EDIT:
The version that has the suggestions included can be found here. I applied the BSD 3-clause license to it.
Separation of concerns
You have the members of your class as shared_ptrs this indicates that you want shallow copy assignment and construction. This is not something that you should concern yourself when designing the class.
I would recommend that you design your queue as non-copyable and non-assignable. Because assigning a queue makes no sense. If the user needs to pass a queue around, it is their responsibility to make a shared_ptr&lt;AsyncQueue&gt;. This has the benefit of making your implementation cleaner, but also performing better as your member variables will now get a better cache locality.
The big 5
Following the above the big 5 should now be:
        AsyncQueue(const AsyncQueue&amp; other) = delete;
        AsyncQueue(AsyncQueue&amp;&amp; other) = delete;
        AsyncQueue&amp; operator=(const AsyncQueue&amp; other) = delete;
        AsyncQueue&amp; operator=(AsyncQueue&amp;&amp; other) = delete;
        virtual ~AsyncQueue() = default;

(note delete instead of default). The move assignment and construction cannot be implemented as moving a mutex is not supported (for good reason).
Use make_shared
If you still insist on using shared_ptr, you should use make_shared to construct them because this will co-allocate the reference count with the object data. This saves some memory but more importantly it improves data locality.
Forwarding references
You're better off using forwarding references (also known as universal reference but forwarding reference is the new preferred name) for the push method. In fact I would go as far as to not provide push but instead provide emplace that constructs the object in place like this:
template&lt;typename... Args&gt;
void emplace(Args&amp;&amp;... args){
    {
        std::lock_guard&lt;std::mutex&gt; lock(*mutex);
        queue-&gt;emplace(std::forward&lt;Args&gt;(args)...);
    }
    cond-&gt;notify_one();
}

emplace() will construct the queued object in place using the arguments passed to emplace() as constructor arguments to avoid a copy. This way you leave the decision whether or not to move to your user.
Obj o;
asyncqueue.emplace(o); // Will copy construct.
asyncqueue.emplace(std::move(o)); // Will move construct.

// Will evaluate o*3 into a temporary and move from it. 
// Possibly eliding the copy.
asyncqueue.emplace( o * 3); 
asyncqueue.emplace(&quot;whatevz&quot;); // Will call constructor taking c-string.

Returning by move in pop
The reason you're crashing is that the local value you moved out of is destroyed when you leave the function scope. So you're moving from rubbish. Returning by r-value reference: Just don't. The compiler will likely perform (N)RVO and the copy will be optimized out if it is possible.
Condtional wait
You have an infinite conditional wait. It may be useful to have a functionality to &quot;sever&quot; the queue and release all threads waiting on jobs.
Typically you would have:
std::atomic&lt;bool&gt; still_running{true};

void stop(){ still_running = false; cond-&gt;notify_all();}

void push(T&amp;&amp;){
   if(!still_running) throw error(&quot;severed pipe&quot;);
   ...
}

void pop(){
   ...
   while(queue-&gt;empty() &amp;&amp; still_running) {
       cond-&gt;wait(lock);
   }
   if(queue-&gt;empty(){
       // Check for queue empty here, as still_running could
       // have been changed after we returned but we could still
       // return useful data.
       throw error(&quot;severed pipe&quot;);
   }
   ...
}

Also prefer the more concise way of waiting on a condition variable:
while(queue-&gt;empty()) {
    cond-&gt;wait(lock);
}

could be:
cond-&gt;wait(lock, [this](){ return !queue-&gt;empty();});

For reference, you might be interested in: my implementation of a similar class