A concurrent queue that supports exactly two threads and not more. One thread is allowed to push, the other to pop My header file implementation:

#ifndef CIRCULARSPSC_H
#define CIRCULARSPSC_H

#include &lt;atomic&gt;
#include &lt;cstddef&gt;

template&lt;typename Data, size_t Size&gt; 
class CircularSpsc
{
public:
    enum { queue_size = Size + 1 };

    CircularSpsc() : back(0), front(0){}
    virtual ~CircularSpsc() {}

    bool push_back(const Data&amp; item)    ;
    bool pop_front(Data&amp; item);

    bool isEmpty() const;
    bool isFull() const;

private:
    size_t increment(size_t idx) const;
    std::atomic&lt;size_t&gt; back;
    Data dataArray[queue_size];
    std::atomic&lt;size_t&gt; front;
};

#include "circularspsc.cpp"

#endif


My source file implementation:

#ifndef CIRCULARSPSC_CPP
#define CIRCULARSPSC_CPP

#include "circularspsc.hpp"

template&lt;typename Data, size_t Size&gt;
bool CircularSpsc&lt;Data, Size&gt;::push_back(const Data&amp; item)
{   
    const auto current_back = back.load(std::memory_order_relaxed);

    const auto next_back = increment(current_back);
    if(next_back != front.load(std::memory_order_acquire))                           
    {   
        dataArray[current_back] = item;
        back.store(next_back, std::memory_order_release); 
        return true;
    }
    return false;
}

template&lt;typename Data, size_t Size&gt;
bool CircularSpsc&lt;Data, Size&gt;::pop_front(Data&amp; item)
{
    const auto currentfront = front.load(std::memory_order_relaxed);
    if(currentfront == back.load(std::memory_order_acquire))
        return false;

    item = dataArray[currentfront];
    front.store(increment(currentfront), std::memory_order_release);
    return true;
}

template&lt;typename Data, size_t Size&gt;
bool CircularSpsc&lt;Data, Size&gt;::isEmpty() const
{
    return (front.load() == back.load());
}

template&lt;typename Data, size_t Size&gt;
bool CircularSpsc&lt;Data, Size&gt;::isFull() const
{
    const auto nextback = increment(back.load());
    return (nextback == front.load());
}

template&lt;typename Data, size_t Size&gt;
size_t CircularSpsc&lt;Data, Size&gt;::increment(size_t idx) const
{
    return (idx + 1) % queue_size;
}

#endif


Client file implementation:

#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;vector&gt;
#include "circularspsc.hpp"
#include "dataobject.hpp"

#define QUEUESIZE 10000

void reader(CircularSpsc&lt;int, QUEUESIZE&gt;&amp; cspsc_q)
{
    for(int i = 0; i &lt; QUEUESIZE; i++)
    {
        int dataItem;
        cspsc_q.pop_front(dataItem);
        std::cout &lt;&lt; "popped value: " &lt;&lt; dataItem &lt;&lt; std::endl;
    }
}

void writer(CircularSpsc&lt;int, QUEUESIZE&gt;&amp; cspsc_q)
{
    for(int i = 0; i &lt; QUEUESIZE; i++)
    {
        cspsc_q.push_back(i);
    }
}

int main()
{
    CircularSpsc&lt;int, QUEUESIZE&gt; cspsc_q;

    std::thread writer_thread(writer, std::ref(cspsc_q));
    std::thread reader_thread(reader, std::ref(cspsc_q));

    reader_thread.join();
    writer_thread.join();
}


This has the clear drawback that we can only create queues of very limited size (because with larger values of QUEUESIZE I am getting segfault (surely because the stack size is limited?)). In my next iteration I plan to implement a version with dynamic memory allocation inside the constructor of the queue for QUEUESIZE number of objects and then use placement new to move values inside the queue.
Naming

I don't understand what CircularSpsc is supposed to mean, and how to even pronounce it (circular space?). I suggest you use a full name without omitting the vowels.

Include guards

You were right to add an include guard to the header file (.hpp). It is supposed to prevent the header file to be included more than once in a single translation unit, which could cause "multiple definition" errors.

However, in the implementation file (.cpp) an include guard is not needed, because this file is not supposed to be included at all.

The implementation file is in fact included at the end of the header file, but this is not the right way to get it compiled. Instead, you should pass its name directly to the compiler in addition to the "main" .cpp file.

You can read more about how compilation works in C++ here, for example.