C++ concurrent queue and testing I've written concurrent queue based on std::queue.

#include &lt;queue&gt;
#include &lt;shared_mutex&gt;
#include &lt;condition_variable&gt;

template &lt;typename T, typename S = typename std::queue&lt;T&gt;::container_type&gt;
class concurrent_queue {
    // no std::shared_mutex yet
    using mutex_type = std::shared_timed_mutex;

public:
    explicit concurrent_queue(const S &amp;sequence)
            : queue_(sequence) { }

    explicit concurrent_queue(S &amp;&amp;sequence = S())
            : queue_(std::move(sequence)) { }

    concurrent_queue(const concurrent_queue &amp;other)
    {
        std::shared_lock&lt;mutex_type&gt; other_lock(other.mutex_);
        queue_ = other.queue_;
    }

    concurrent_queue(concurrent_queue &amp;&amp;other)
    {
        std::unique_lock&lt;mutex_type&gt; lock(mutex_, std::defer_lock);
        std::shared_lock&lt;mutex_type&gt; other_lock(other.mutex_, std::defer_lock);
        std::lock(lock, other_lock);
        queue_ = std::move(other.queue_);
    }

    concurrent_queue &amp;operator=(const concurrent_queue &amp;other)
    {
        if (this != &amp;other) {
            std::unique_lock&lt;mutex_type&gt; lock(mutex_, std::defer_lock);
            std::shared_lock&lt;mutex_type&gt; other_lock(other.mutex_, std::defer_lock);
            std::lock(lock, other_lock);
            queue_ = other.queue_;
        }
        return *this;
    }

    concurrent_queue &amp;operator=(concurrent_queue &amp;&amp;other)
    {
        if (this != &amp;other) { // not needed?
            std::unique_lock&lt;mutex_type&gt; lock(mutex_, std::defer_lock);
            std::shared_lock&lt;mutex_type&gt; other_lock(other.mutex_, std::defer_lock);
            std::lock(lock, other_lock);
            queue_ = std::move(other.queue_);
        }
        return *this;
    }

    ~concurrent_queue() = default;

    T pop()
    {
        std::unique_lock&lt;mutex_type&gt; lock(mutex_);
        condition_variable_.wait(lock, [this] { return !queue_.empty(); });
        T item = queue_.front(); // use auto?
        queue_.pop();
        return item;
    }

    bool pop(T &amp;item)
    {
        std::unique_lock&lt;mutex_type&gt; lock(mutex_);
        if (queue_.empty()) {
            return false;
        } else {
            item = queue_.front();
            queue_.pop();
            return true;
        }
    }

    void push(const T &amp;item)
    {
        std::unique_lock&lt;mutex_type&gt; lock(mutex_);
        queue_.push(item);
        lock.unlock();
        condition_variable_.notify_one();
    }

    void push(T &amp;&amp;item)
    {
        std::unique_lock&lt;mutex_type&gt; lock(mutex_);
        queue_.push(std::move(item));
        lock.unlock();
        condition_variable_.notify_one();
    }

    template &lt;typename ...Args&gt;
    void emplace(Args &amp;&amp;... args)
    {
        std::unique_lock&lt;mutex_type&gt; lock(mutex_);
        queue_.emplace(std::forward&lt;Args&gt;(args)...);
        lock.unlock();
        condition_variable_.notify_one();
    }

private:
    mutable mutex_type mutex_;
    std::condition_variable_any condition_variable_;
    std::queue&lt;T, S&gt; queue_;
};


Things I'd like to know about this code:


Possible deadlocks - it seems to be working fine in my program, but I'm only using one producer and one consumer. (see #3)
Exception safety - I guess pop methods can throw, but I don't see how this could be avoided without hacks like external locking (lock-front-pop-unlock)
Testing - I've done some really basic tests, things like multiple readers and writers. It has never crashed but I don't really know how to test it.
Anything that comes to your mind.
The two pop() functions have very different semantics, this is probably more of a taste issue, but i'd normally like to see such a difference in a difference in function name. Yes the signature is different, but there isn't really anything that would tell a user that one is blocking and the other one isn't. 

With the copy and move operations, it might help if you thought about the semantics, when does one usually copy an object, when is it moved. Does the expectation that a user has from a copy constructor or the move constructor match what you are doing. It sounds like with the move operator you have some different functionality in mind. If that is that case I'd normally put that in a separate function. Also think of how this queue should be used, is a move or a copy a valid operation.