Debug-code
The proper destination for debug-output is STDERR
std::clog
(which goes toSTDERR
by default,std::cerr
has the same destination but no buffering), not STDOUTstd::cout
(which goes toSTDOUT
). Doubly so for trace-output.I guess you resort to
std::endl
because you use the wrong stream, though that's still a poor excuse. If you need an explicit flush, be explicit and usestd::flush
. Don't leave us wondering if you just wanted a newline.Get rid of all the conditional compilation using the preprocessor.
While it might make sense to allow tracing your calls, code it similar toassert()
or at least get rid of it at the end. Also, don't hang it onNDEBUG
, tracing is far more intrusive.
// trace.h #ifdef TRACE #undef trace #include <iostream> template<class...Ts> inline void trace(Ts const&... ts) noexcept { using T = int[]; T{0, (std::cerr << ts, void(), 0)}; } #define trace(...) trace(__VA_ARGS__) #else #define trace(...) void() #endif
Model your type on the standard library as good as you can. An interface should be as unsurprising as it can be without burdening the implementation unduly.
- Put the templates type-argument first. Everyone else does it.
- Use a
std::size_t
for the size like everyone else.
Also, if you don't want to name itN
, use something more descriptive likeCAPACITY
. - Allow initialization from arbitrary iterator-pairs. That can be leveraged for
std::initializer_list
. - Support move- and copy-insertion for
.push_back()
. The easiest way is by supporting.emplace_back()
, as you don't need to special-case copying an element and re-allocation. - For
.insert()
, create and delegate to.emplace()
. .pop_back()
should not return anything, because copying the removed element can be a costly waste of time.operator[]
needs a constant overload, and should not catch errors. Adding anassert()
would be appropriate though.
.at()
is the member which should check bounds..back()
also misses its constant overload, and should at most contain anassert()
for checking errors.
Without.front()
it seems lonely.- You are missing the whole iterator-interface, among others. At least add a note:
// TODO: iterators and more
.
Polishing the interface
Just use
= default;
instead of{}
for the default ctor. Some code checks for trivial ctors and provides an optimized path.Use
noexcept
where expected. Doing so allows the compiler to remove exception-handling code, and there are often faster correct paths for code which cannot throw.
Working with raw memory
Don't initialize a bunch of objects you might never need. Use an unnamed union for the data-array to suppress the compiler calling special functions.
union { T internal_buffer[BUFFER_SIZE]; };
Yes, the above point means that the default copy- and move- ctor/assignment as well as the default dtor do the wrong thing. Define them yourself to do the right thing.
Even if you do not want to use the Uninitialized memory algorithms, you can use placement-new and manual destructor-invocation directly. Just include
<new>
.
(void)
is the proper parameter-list for a no argument function in C, due to back-compatibility. While C++ accepts it,()
is preferred.
Debug-code
The proper destination for debug-output is STDERR, not STDOUT. Doubly so for trace-output.
I guess you resort to
std::endl
because you use the wrong stream, though that's still a poor excuse. If you need an explicit flush, be explicit and usestd::flush
. Don't leave us wondering if you just wanted a newline.Get rid of all the conditional compilation using the preprocessor.
While it might make sense to allow tracing your calls, code it similar toassert()
or at least get rid of it at the end. Also, don't hang it onNDEBUG
, tracing is far more intrusive.
// trace.h #ifdef TRACE #undef trace #include <iostream> template<class...Ts> inline void trace(Ts const&... ts) noexcept { using T = int[]; T{0, (std::cerr << ts, void(), 0)}; } #define trace(...) trace(__VA_ARGS__) #else #define trace(...) void() #endif
Model your type on the standard library as good as you can. An interface should be as unsurprising as it can be without burdening the implementation unduly.
- Put the templates type-argument first. Everyone else does it.
- Use a
std::size_t
for the size like everyone else.
Also, if you don't want to name itN
, use something more descriptive likeCAPACITY
. - Allow initialization from arbitrary iterator-pairs. That can be leveraged for
std::initializer_list
. - Support move- and copy-insertion for
.push_back()
. The easiest way is by supporting.emplace_back()
, as you don't need to special-case copying an element and re-allocation. - For
.insert()
, create and delegate to.emplace()
. .pop_back()
should not return anything, because copying the removed element can be a costly waste of time.operator[]
needs a constant overload, and should not catch errors. Adding anassert()
would be appropriate though.
.at()
is the member which should check bounds..back()
also misses its constant overload, and should at most contain anassert()
for checking errors.
Without.front()
it seems lonely.- You are missing the whole iterator-interface, among others. At least add a note:
// TODO: iterators and more
.
Polishing the interface
Just use
= default;
instead of{}
for the default ctor. Some code checks for trivial ctors and provides an optimized path.Use
noexcept
where expected. Doing so allows the compiler to remove exception-handling code, and there are often faster correct paths for code which cannot throw.
Working with raw memory
Don't initialize a bunch of objects you might never need. Use an unnamed union for the data-array to suppress the compiler calling special functions.
union { T internal_buffer[BUFFER_SIZE]; };
Yes, the above point means that the default copy- and move- ctor/assignment as well as the default dtor do the wrong thing. Define them yourself to do the right thing.
Even if you do not want to use the Uninitialized memory algorithms, you can use placement-new and manual destructor-invocation directly. Just include
<new>
.
(void)
is the proper parameter-list for a no argument function in C, due to back-compatibility. While C++ accepts it,()
is preferred.
Debug-code
The proper destination for debug-output is
std::clog
(which goes toSTDERR
by default,std::cerr
has the same destination but no buffering), notstd::cout
(which goes toSTDOUT
). Doubly so for trace-output.I guess you resort to
std::endl
because you use the wrong stream, though that's still a poor excuse. If you need an explicit flush, be explicit and usestd::flush
. Don't leave us wondering if you just wanted a newline.Get rid of all the conditional compilation using the preprocessor.
While it might make sense to allow tracing your calls, code it similar toassert()
or at least get rid of it at the end. Also, don't hang it onNDEBUG
, tracing is far more intrusive.
// trace.h #ifdef TRACE #undef trace #include <iostream> template<class...Ts> inline void trace(Ts const&... ts) noexcept { using T = int[]; T{0, (std::cerr << ts, void(), 0)}; } #define trace(...) trace(__VA_ARGS__) #else #define trace(...) void() #endif
Model your type on the standard library as good as you can. An interface should be as unsurprising as it can be without burdening the implementation unduly.
- Put the templates type-argument first. Everyone else does it.
- Use a
std::size_t
for the size like everyone else.
Also, if you don't want to name itN
, use something more descriptive likeCAPACITY
. - Allow initialization from arbitrary iterator-pairs. That can be leveraged for
std::initializer_list
. - Support move- and copy-insertion for
.push_back()
. The easiest way is by supporting.emplace_back()
, as you don't need to special-case copying an element and re-allocation. - For
.insert()
, create and delegate to.emplace()
. .pop_back()
should not return anything, because copying the removed element can be a costly waste of time.operator[]
needs a constant overload, and should not catch errors. Adding anassert()
would be appropriate though.
.at()
is the member which should check bounds..back()
also misses its constant overload, and should at most contain anassert()
for checking errors.
Without.front()
it seems lonely.- You are missing the whole iterator-interface, among others. At least add a note:
// TODO: iterators and more
.
Polishing the interface
Just use
= default;
instead of{}
for the default ctor. Some code checks for trivial ctors and provides an optimized path.Use
noexcept
where expected. Doing so allows the compiler to remove exception-handling code, and there are often faster correct paths for code which cannot throw.
Working with raw memory
Don't initialize a bunch of objects you might never need. Use an unnamed union for the data-array to suppress the compiler calling special functions.
union { T internal_buffer[BUFFER_SIZE]; };
Yes, the above point means that the default copy- and move- ctor/assignment as well as the default dtor do the wrong thing. Define them yourself to do the right thing.
Even if you do not want to use the Uninitialized memory algorithms, you can use placement-new and manual destructor-invocation directly. Just include
<new>
.
(void)
is the proper parameter-list for a no argument function in C, due to back-compatibility. While C++ accepts it,()
is preferred.
Debug-code
The proper destination for debug-output is STDERR, not STDOUT. Doubly so for trace-output.
I guess you resort to
std::endl
because you use the wrong stream, though that's still a poor excuse. If you need an explicit flush, be explicit and usestd::flush
. Don't leave us wondering if you just wanted a newline.Get rid of all the conditional compilation using the preprocessor.
While it might make sense to allow tracing your calls, code it similar toassert()
or at least get rid of it at the end. Also, don't hang it onNDEBUG
, tracing is far more intrusive.
// trace.h #ifdef TRACE #undef trace #include <iostream> template<class...Ts> inline void trace(Ts const&... ts) noexcept { using T = int[]; T{0, (std::cerr << ts, void(), 0)}; } #define trace(...) trace(__VA_ARGS__) #else #define trace(...) void() #endif
Model your type on the standard library as good as you can. An interface should be as unsurprising as it can be without burdening the implementation unduly.
- Put the templates type-argument first. Everyone else does it.
- Use a
std::size_t
for the size like everyone else.
Also, if you don't want to name itN
, use something more descriptive likeCAPACITY
. - Allow initialization from arbitrary iterator-pairs. That can be leveraged for
std::initializer_list
. - Support move- and copy-insertion for
.push_back()
. The easiest way is by supporting.emplace_back()
, as you don't need to special-case copying an element and re-allocation. - For
.insert()
, create and delegate to.emplace()
. .pop_back()
should not return anything, because copying the removed element can be a costly waste of time.operator[]
needs a constant overload, and should not catch errors. Adding anassert()
would be appropriate though.
.at()
is the member which should check bounds..back()
also misses its constant overload, and should at most contain anassert()
for checking errors.
Without.front()
it seems lonely.- You are missing the whole iterator-interface, among others. At least add a note:
// TODO: iterators and more
.
Polishing the interface
Just use
= default;
instead of{}
for the default ctor. Some code checks for trivial ctors and provides an optimized path.Use
noexcept
where expected. Doing so allows the compiler to remove exception-handling code, and there are often faster correct paths for code which cannot throw.
Working with raw memory
Don't initialize a bunch of objects you might never need. Use an unnamed union for the data-array to suppress the compiler calling special functions.
union { T internal_buffer[BUFFER_SIZE]; };
Yes, the above point means that the default copy- and move- ctor/assignment as well as the default dtor do the wrong thing. Define them yourself to do the right thing.
Even if you do not want to use the Uninitialized memory algorithms, you can use placement-new and manual destructor-invocation directly. Just include
<new>
.
(void)
is the proper parameter-list for a no argument function in C, due to back-compatibility. While C++ accepts it,()
is preferred.