Queue implementation using unique_ptr
Yes I think your usage of unique_ptr is correct. The only recommendation I would make is to use make_unique for construction:

explicit Queue(std::size_t space) : 
    q_ptr(std::make_unique&lt;T[]&gt;(space)), 
    capacity(space) {}


This answer provides motivation for its use. I've also used explicit to avoid implicit conversion from size_t to Queue. And I made use of a member initialization list.

I wrote the following tests using googletest:

TEST(QueueTester, testEmpty) {
    Queue&lt;int&gt; queue(0);
    EXPECT_EQ(0, queue.size());
}

TEST(QueueTester, testSingle) {
    Queue&lt;int&gt; queue(1);
    EXPECT_EQ(0, queue.size());
    queue.push(0);
    EXPECT_EQ(1, queue.size());
}


But neither passed. I corrected this by using the following:

int front_idx = 0;


And changing push:

template&lt;typename T&gt;
void Queue&lt;T&gt;::push(T value)
{
    if (back_idx - front_idx + 1 == capacity)
    {
        std::cerr &lt;&lt; "Queue full\n";
        return;
    }
    q_ptr[++back_idx] = value;
}


I changed front and back to return references to the elements to allow the user to modify the contents. I also wrote overloads for returning const references to allow reading when the Queue is passed by const reference:

template&lt;typename T&gt;
T &amp;Queue&lt;T&gt;::front()
{
    return q_ptr[front_idx];
}

template&lt;typename T&gt;
T &amp;Queue&lt;T&gt;::back()
{
    return q_ptr[back_idx];
}

template&lt;typename T&gt;
const T &amp;Queue&lt;T&gt;::front() const
{
    return q_ptr[front_idx];
}

template&lt;typename T&gt;
const T &amp;Queue&lt;T&gt;::back() const
{
    return q_ptr[back_idx];
}


I further noticed that push, given a nonzero front_idx, will write past the end of the array pointed to by q_ptr. I took inspiration from this page and modified the code to store elements circularly around the array. Here is the final implementation:

#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;cstddef&gt;

template&lt;typename T&gt;
class Queue
{
    std::unique_ptr&lt;T[]&gt; q_ptr;
    int front_idx = 0;
    int back_idx = -1;
    std::size_t _size = 0;
    std::size_t capacity;
public:
    explicit Queue(std::size_t space) :
        q_ptr(std::make_unique&lt;T[]&gt;(space)),
        capacity(space)
    {
    }
    void push(T value);
    void pop();
    bool full();
    bool empty();
    T &amp;front();
    T &amp;back();
    const T &amp;front() const;
    const T &amp;back() const;
    std::size_t size() const;
};

template&lt;typename T&gt;
void Queue&lt;T&gt;::push(T value)
{
    if (full())
    {
        std::cerr &lt;&lt; "Queue full\n";
        return;
    }
    back_idx = (back_idx + 1) % capacity;
    q_ptr[back_idx] = value;
    ++_size;
}

template&lt;typename T&gt;
bool Queue&lt;T&gt;::full()
{
    return _size == capacity;
}

template&lt;typename T&gt;
void Queue&lt;T&gt;::pop()
{
    if (empty())
    {
        std::cerr &lt;&lt; "Empty queue\n";
        return;
    }
    q_ptr[front_idx] = T{};
    front_idx = (front_idx + 1) % capacity;
    --_size;
}

template&lt;typename T&gt;
bool Queue&lt;T&gt;::empty()
{
    return _size == 0;
}

template&lt;typename T&gt;
T &amp;Queue&lt;T&gt;::front()
{
    return q_ptr[front_idx];
}

template&lt;typename T&gt;
T &amp;Queue&lt;T&gt;::back()
{
    return q_ptr[back_idx];
}

template&lt;typename T&gt;
const T &amp;Queue&lt;T&gt;::front() const
{
    return q_ptr[front_idx];
}

template&lt;typename T&gt;
const T &amp;Queue&lt;T&gt;::back() const
{
    return q_ptr[back_idx];
}

template&lt;typename T&gt;
std::size_t Queue&lt;T&gt;::size() const
{
    return _size;
}