К современному коду предъявляется немало требований. В частности, код должен отвечать определенным критериям качества чтобы иметь шансы на добавление в основной репозиторий в качестве решения задачи.
Среди таких критериев качества нередко бывают: соответствие общему стилю кода, соответствие общему стилю форматирования, наличие документации или самодокументируемость, а так же - элементарная понятность для читателя.
Читатель современного кода не должен задаваться вопросами: что тут делает ядовитый газ, зачем швабры и почему вентилятор такой большой [
?].
Читателю все должно быть понятно практически сразу, у него должно возникать как можно меньше вопросов.
Вопросы у читателя возникают тогда, когда он видит что-то нелогичное конкретно для данного участка кода.
Когда читатель изучает код циклического буфера, он у себя в голове держит правило, согласно которому добавляемые элементы будут или добавлены в незанятую память, или будут замещать уже расположенные в памяти элементы.
Эту логику для читателя лучше не ломать, а то пойдут вопросы. Поэтому, самым логичным образом будет именно замещать состояние уже размещенного объекта новым используя оператор перемещения.
*reinterpret_cast<T*>( &buffer[ sizeof( T ) * head ] ) = T{ std::forward<Args>( args )... };
Да, тут присутствует
Value Initialization и оператор перемещения. Однако, оптимизация этого кода сведет все к довольно короткому и как можно более быстрому коду. Поэтому не стоит беспокоиться об этом на текущем этапе. Лучше беспокоиться о понятности кода для читателя.
Столь же понятным будет и такой код:
auto T vicar{ std::forward<Args>( args )... };
std::swap( *reinterpret_cast<T*>( &buffer[ sizeof( T ) * head ] ), vicar );
Он не будет вызывать много вопросов, разве только вопрос относительно использования
std::swap
, но только от людей со слабой привычкой пользоваться STL.
Однако, такой код может привести к ошибке трансляции в том случае, если
T
является неперемещаемым. В твоем ЦБ должны присутствовать проверки на перемещаемость, копируемость и возможность размена состояний.
Если
T
можно копировать, но не перемещать, использовать стоит оператор копирования.
И только если ни копировать, ни перемещать
T
нельзя, следует пользоваться деструктором и размещающим конструктором.
Я бы рекомендовал все три действия оформить в виде перегрузок шаблона функции со SFINAE, чтобы для любого
T
включалась только одна конкретная перегрузка шаблона.