constant sized circular (ring) buffer I've implemented constant sized circular buffer. It provides push and pop operations, along with modern emplace. It is also optimized for destruction of trivially destructible types.

I didn't include iterators since it would be strange to have one pair of begin and end for input iterators and other for output iterators. Allocator support will come in the next version.

I'd like the review to focus on performance of push (emplace) and pop operations. Also, there is a code duplication in those functions. Should I leave just emplace?

#ifndef CIRCULAR_BUFFER_H
#define CIRCULAR_BUFFER_H

#include &lt;cstddef&gt;
#include &lt;utility&gt;

template &lt;typename T, std::size_t mcapacity&gt;
class circular_buffer
{
    T* data;
    T* begin;
    T* end;
    std::size_t msize;
    bool is_empty;

    template &lt;bool trivially_destructible&gt;
    struct destructor
    {
        void destroy(T* object)
        {
            object-&gt;~T();
        }

        void destroy_n(T* objects, std::size_t sz)
        {
            for (std::size_t i = 0; i &lt; sz; ++i)
            {
                objects-&gt;~T();
                ++objects;
            }

            ::operator delete(objects);
        }
    };

    template &lt;&gt;
    struct destructor&lt;true&gt;
    {
        void destroy(T* object)
        {
            //empty, since trivially destroyable
        }

        void destroy_n(T* objects, std::size_t sz)
        {
            ::operator delete(objects);
        }
    };

    destructor&lt;std::is_trivially_destructible&lt;T&gt;::value&gt; destroyer;

public:
    using container = circular_buffer&lt;T, mcapacity&gt;;
    using value_type = T;
    using reference = value_type&amp;;
    using pointer = T*;
    using const_reference = const reference;
    using size_type = std::size_t;

    circular_buffer(): 
        data((pointer)::operator new(sizeof(value_type) * mcapacity)),
        msize(0),
        begin(data),
        end(data)
    {}

    template &lt;typename ... ArgTypes&gt;
    void emplace(ArgTypes ... args)
    {
        end = new (end) T(args...);
        ++end;
        if (end == data + mcapacity)
        {
            end = data;
        }

        if (msize != mcapacity)
        {
            ++msize;
        }
    }

    void push(const T&amp; value)
    {
        end = new (end) T(value);
        ++end;
        if (end == data + mcapacity)
        {
            end = data;
        }

        if (msize != mcapacity)
        {
            ++msize;
        }
    }

    void push(T&amp;&amp; value)
    {
        end-&gt;T(value);
        ++end;
        if (end == data + mcapacity)
        {
            end = data;
        }

        if (msize != mcapacity)
        {
            ++msize;
        }
    }

    const_reference front()
    {
        return *begin;
    }

    void pop()
    {
        destroyer.destroy(begin);
        ++begin;
        if (begin == data + mcapacity)
        {
            begin = data;
        }

        if (msize != 0)
        {
            --msize;
        }
    }

    size_type size()
    {
        return msize;
    }

    constexpr size_type capacity()
    {
        return mcapacity;
    }

    bool full()
    {
        return msize == mcapacity;
    }

    bool empty()
    {
        return msize == 0;
    }

    ~circular_buffer()
    {
        destroyer.destroy_n(data, mcapacity);
    }
};

#endif


Sample usage:

#include "circular_buffer.h"
#include &lt;vector&gt;
#include &lt;iostream&gt;

int main()
{   
    circular_buffer&lt;int, 10&gt; buffer;

    for (int i = 0; i &lt; 30; ++i)
    {
        buffer.push(i);
    }

    std::cout &lt;&lt; buffer.full() &lt;&lt; '\n'
        &lt;&lt; buffer.empty() &lt;&lt; '\n';

    for (int i = 0; i &lt; 20; ++i)
    {
        std::cout &lt;&lt; buffer.front() &lt;&lt; ' ';
        buffer.pop();
    }

    std::cout &lt;&lt; buffer.full() &lt;&lt; '\n'
        &lt;&lt; buffer.empty() &lt;&lt; '\n';
}
Type aliases


The container alias is never used, it can be removed.
The const_reference alias is wrong, it is still an alias to T&amp;.
Use a consistent aliasing scheme, only use T when aliasing value_type.
Provide a const_pointer alias.


With these points in mind, your type aliases become:

using value_type = T;
using reference = value_type&amp;;
using const_reference = value_type const&amp;; // is now actually reference-to-const alias
using pointer = value_type*; // use value_type alias
using const_pointer = value_type const*; // for iterators, casting, etc.
using size_type = std::size_t;


Consider the rule of 5 when your class manages a resource.

Whenever your class manages a resource, it probably needs to implement the destructor, copy and move operations. You definitely do not want pointers to be copied when a copy of circular_buffer&lt;&gt; is made. Possible issues:


Memory being freed twice.
Dangling references.
Invalid object states.


Use static memory when the size is known at compile-time.

This will be more efficient and exception-free during allocation. Possible replacement:

#include &lt;type_traits&gt;
std::aligned_storage_t&lt;mcapacity * sizeof( T ), alignof( T )&gt; data;


Now that you are using static memory, you can provide two private functions to avoid having to use ugly casts everywhere:

T* data() { return reinterpret_cast&lt;T*&gt;( &amp;data ); }
T const* data() const { return reinterpret_cast&lt;T const*&gt;( &amp;data ); }


Your constructor becomes:

circular_buffer() noexcept /* no exceptions with static memory allocation */ :
    /* members reordered to match class */
    begin( data() ),
    end( begin ),
    msize( 0 ),
    is_empty( false )
{}



We now explicitly initialize is_empty in the constructor initializer list. I think this is clearer.
To avoid weird bugs, members are initialized in the same order that they
are declared in the class.
If your class needs really big chunks of memory that aren't available statically, then don't make the capacity a template parameter: make it a constructor parameter.


Use universal references in the definition of emplace().

This avoids unnecessary copying of arguments. Replacement:

template &lt;typename... ArgTypes&gt;
void emplace( ArgTypes&amp;&amp;... args ); // variadic template of universal references


This change requires that we also perfect-forward these universal references wherever they may be used:

T{ std::forward&lt;ArgTypes&gt;( args )... }; // perfect-forward example


Use std::move() when you're finished with rvalue arguments.

In push( T&amp;&amp; ), argument value is actually used as an T const&amp; (which calls the copy constructor) because you forget to call std::move(). The fix is simple:

end-&gt;T( value );


Becomes:

end-&gt;T( std::move( value ) );


Factor out repeated code.

Your push() overloads and emplace() have the exact same block of code being repeated; this block is a prime candidate for being extracted into a private function:

void tidy() noexcept // feel free to use a descriptive function name
{ // the repeating block of code
    ++end;

    if ( end == data + mcapacity )
        end = data;

    if ( msize != mcapacity )
        ++msize;
}


After modifications, emplace() and push() now look like this:

template &lt;typename... ArgTypes&gt;
void emplace( ArgTypes&amp;&amp;... args )
{
    ::new ( end ) T( std::forward&lt;ArgTypes&gt;( args )... );
    tidy();
}

void push( T const&amp; value )
{
    ::new ( end ) T( value );
    tidy();
}

void push( T&amp;&amp; value )
{
    ::new ( end ) T( std::move( value ) );
    tidy();
}


Note the consistent use of the same construction approach (placement-new) which looks nice and prevents syntax issues with primitive types.

Mark functions as noexcept when appropriate.

Non-throwing functions of yours include size(), capacity() and a few more. This will help programmers know that certain functions do not throw, which can help with optimizations.

For compile-time conditional noexcept marking, use type traits. Here's a sample:

template &lt;typename... ArgTypes&gt;
void emplace( ArgTypes&amp;&amp;... args )
noexcept( std::is_nothrow_constructible&lt;T, ArgTypes&amp;&amp;...&gt;::value )
{ /* ... */ }


Mark non-modifying functions as const.

const functions of yours include front(), size() and a few more. Member functions that don't modify any of the class data members should be marked as const so that they can be used in the appropriate scenario (accessed through a circular_buffer&lt;&gt; const&amp;).