patterncppMinor
A Slice-type for C++
Viewed 0 times
typeforslice
Problem
The Go programming language has that slice type, which is under the hood a pointer to an actual array. This allows treating the subarrays as actual arrays with almost no performance overhead. This code snippet is about implementing slice type for C++ arrays and vectors.
slice.h:
main.cpp:
Any critique is m
slice.h:
#ifndef SLICE_H
#define SLICE_H
#include
#include
#include
template
class slice {
private:
T* m_array;
size_t m_length;
public:
slice(T* array, size_t length) {
this->m_array = array;
this->m_length = length;
}
slice(T* array, size_t length, size_t skip) {
this->m_array = array + skip;
this->m_length = length;
}
T& operator[](size_t index) {
if (index >= m_length) {
std::ostringstream os;
os << "Bad index: " << index << ", slice size: " << m_length;
throw std::runtime_error(os.str());
}
return m_array[index];
}
size_t size() {
return m_length;
}
T* begin() {
return m_array;
}
T* end() {
return m_array + m_length;
}
const T* begin() const {
return m_array;
}
const T* end() const {
return m_array + m_length;
}
};
#endif /* SLICE_H */main.cpp:
#include
#include
#include
#include "slice.h"
using std::cout;
using std::endl;
using std::vector;
int main(int argc, char** argv) {
int int_array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
//// Should be 4, 5, 6, 7, 8.
slice pizza_slice = slice(int_array, 5, 3);
cout int_vector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
//// Should be 2, 3, 4
pizza_slice = slice(&int_vector[0], 3, 1);
cout << "Vector slice size: " << pizza_slice.size() << endl;
for (int i : pizza_slice)
{
cout << i << endl;
}
return 0;
}Any critique is m
Solution
This looks pretty good.
Use mem-initializer lists
When you write
you're default constructing
Your other constructor can on the one hand just delegate to this one:
but on the other hand doesn't really need to exist anyway. Just let the user pass in where he wants to start. This strikes me as just bloaty.
const functions
You could also provide a
Additionally, consider providing a different constructor so that I can construct a
throwing
If you're throwing due to out of range indexing, you should throw
constexpr
Everything here can be
Use mem-initializer lists
When you write
slice(T* array, size_t length) {
this->m_array = array;
this->m_length = length;
}you're default constructing
m_array and m_length and then assigning them. For pointers and integral types, this isn't exactly expensive, but it's a waste and may as well not get into that habit. Prefer directly constructing:slice(T* array, size_t length)
: m_array(array)
, m_length(length)
{ }Your other constructor can on the one hand just delegate to this one:
slice(T* array, size_t length, size_t skip)
: slice(array + skip, length)
{ }but on the other hand doesn't really need to exist anyway. Just let the user pass in where he wants to start. This strikes me as just bloaty.
const functions
size() should be const, and you're missing T const& operator[](size_t) const. You could also provide a
T data() and T const data() const, which in this case just alias begin().Additionally, consider providing a different constructor so that I can construct a
slice from a slice. That's something that makes sense. throwing
If you're throwing due to out of range indexing, you should throw
std::out_of_range. But generally, avoid having operator[] throw. This is what you're going to use most often so it's best to make it as simple as possible. If you want to provide a throwing alternative, the typical pattern followed by the standard library is to provide an at() function that does the bounds checking and then forwards along to operator[]. constexpr
Everything here can be
constexpr. Just go overboard.Code Snippets
slice(T* array, size_t length) {
this->m_array = array;
this->m_length = length;
}slice(T* array, size_t length)
: m_array(array)
, m_length(length)
{ }slice(T* array, size_t length, size_t skip)
: slice(array + skip, length)
{ }Context
StackExchange Code Review Q#115319, answer score: 5
Revisions (0)
No revisions yet.