Date: Wed, 2 Sep 2020 23:30:33 +0100
My proposal was intended to allow Arthur’s sample to be written as below.
template<class T, size_t Cap>
class fixed_capacity_vector {
[[unconstructed]] T data_[ Cap];
size_t size_ = 0;
public:
size_t size() const { return size_; }
T* data() { return data_; } // no cast necessary
const T* data() const { return const_cast<const T*>(data_); } // better cast - just a const_cast
T& operator[](int i) { return data_[i]; } // no need for data(), can access array directly
const T& operator[](int i) const { return data_[i]; } // again access array directly.
T& push_back(const T& value) {
assert(size_ < Cap);
T* p = ::new ((void*)&data[size_]) T(value); // not sure if the cast is needed here, don't need the function call.
++size_;
return *p;
}
bool empty()
{
return (size_ == 0);
}
T& back()
{
return data_[size_ - 1]; // again, no need for function.
}
void pop_back() {
back().~T();
--size_;
}
fixed_capacity_vector()
: size_(0)
{}
fixed_capacity_vector(const fixed_capacity_vector& rhs) {
std::uninitialized_copy(rhs.begin(), rhs.end(), data_);
size_ = rhs.size();
}
fixed_capacity_vector& operator=(const fixed_capacity_vector& rhs) {
// left as an exercise for the reader
// also, move semantics left as an exercise for the reader
}
~fixed_capacity_vector() {
while (!empty()) pop_back();
}
};
Sent from Mail for Windows 10
From: Arthur O'Dwyer
Sent: 02 September 2020 22:11
To: Std-Proposals; Steve Hearnden
Subject: Re: [std-proposals] C++ create a class with array members which arenot constructed.
On Wed, Sep 2, 2020 at 2:46 PM Steve Hearnden via Std-Proposals <std-proposals_at_[hidden]> wrote:
I was looking at a performance issue, with some code and found that the cost of creating the dynamic heap memory was the significant cost.
It would have been handy to have a class which implemented something like std::vector, but with a *small* buffer of values to use as an alternative piece of memory when the vector size was below a threshold. [...]
template< class T, size_t size>
class temp_vector
{
[[unconstructed]] T _V[size];
size_t m_constructed = 0;
public:
~temp_vector()
{
///// Destroy the array manually.
for( size_t i = 0; i < m_constructed; i++ ){
_V[i].~T();
}
}
}
I'm amazed that nobody has just given you teh codez yet. Here's basically what you should write:
template<class T, size_t Cap>
class fixed_capacity_vector {
alignas(T) char data_[Cap * sizeof(T)];
size_t size_ = 0;
public:
size_t size() const { return size_; }
T *data() { return (T*)data_; }
const T *data() const { return (const T*)data_; }
T& operator[](int i) { return data()[i]; }
const T& operator[](int i) const { return data()[i]; }
T& push_back(const T& value) {
assert(size_ < Cap);
T *p = ::new ((void*)&data()[size_]) T(value);
++size_;
return *p;
}
void pop_back() {
back().~T();
--size_;
}
fixed_capacity_vector(const fixed_capacity_vector& rhs) {
std::uninitialized_copy(rhs.begin(), rhs.end(), data_);
size_ = rhs.size();
}
fixed_capacity_vector& operator=(const fixed_capacity_vector& rhs) {
// left as an exercise for the reader
// also, move semantics left as an exercise for the reader
}
~fixed_capacity_vector() {
while (!empty()) pop_back();
}
};
Notice the Rule of Three/Five here. If you provide a custom destructor, you should also provide custom copy and move operations (or =delete them).
This is also available fully fleshed out in Boost, in the form of boost::static_vector; and there is an active WG21 proposal, P0843, currently targeting the Library Fundamentals v3 TS.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0843r4.html
HTH,
Arthur
template<class T, size_t Cap>
class fixed_capacity_vector {
[[unconstructed]] T data_[ Cap];
size_t size_ = 0;
public:
size_t size() const { return size_; }
T* data() { return data_; } // no cast necessary
const T* data() const { return const_cast<const T*>(data_); } // better cast - just a const_cast
T& operator[](int i) { return data_[i]; } // no need for data(), can access array directly
const T& operator[](int i) const { return data_[i]; } // again access array directly.
T& push_back(const T& value) {
assert(size_ < Cap);
T* p = ::new ((void*)&data[size_]) T(value); // not sure if the cast is needed here, don't need the function call.
++size_;
return *p;
}
bool empty()
{
return (size_ == 0);
}
T& back()
{
return data_[size_ - 1]; // again, no need for function.
}
void pop_back() {
back().~T();
--size_;
}
fixed_capacity_vector()
: size_(0)
{}
fixed_capacity_vector(const fixed_capacity_vector& rhs) {
std::uninitialized_copy(rhs.begin(), rhs.end(), data_);
size_ = rhs.size();
}
fixed_capacity_vector& operator=(const fixed_capacity_vector& rhs) {
// left as an exercise for the reader
// also, move semantics left as an exercise for the reader
}
~fixed_capacity_vector() {
while (!empty()) pop_back();
}
};
Sent from Mail for Windows 10
From: Arthur O'Dwyer
Sent: 02 September 2020 22:11
To: Std-Proposals; Steve Hearnden
Subject: Re: [std-proposals] C++ create a class with array members which arenot constructed.
On Wed, Sep 2, 2020 at 2:46 PM Steve Hearnden via Std-Proposals <std-proposals_at_[hidden]> wrote:
I was looking at a performance issue, with some code and found that the cost of creating the dynamic heap memory was the significant cost.
It would have been handy to have a class which implemented something like std::vector, but with a *small* buffer of values to use as an alternative piece of memory when the vector size was below a threshold. [...]
template< class T, size_t size>
class temp_vector
{
[[unconstructed]] T _V[size];
size_t m_constructed = 0;
public:
~temp_vector()
{
///// Destroy the array manually.
for( size_t i = 0; i < m_constructed; i++ ){
_V[i].~T();
}
}
}
I'm amazed that nobody has just given you teh codez yet. Here's basically what you should write:
template<class T, size_t Cap>
class fixed_capacity_vector {
alignas(T) char data_[Cap * sizeof(T)];
size_t size_ = 0;
public:
size_t size() const { return size_; }
T *data() { return (T*)data_; }
const T *data() const { return (const T*)data_; }
T& operator[](int i) { return data()[i]; }
const T& operator[](int i) const { return data()[i]; }
T& push_back(const T& value) {
assert(size_ < Cap);
T *p = ::new ((void*)&data()[size_]) T(value);
++size_;
return *p;
}
void pop_back() {
back().~T();
--size_;
}
fixed_capacity_vector(const fixed_capacity_vector& rhs) {
std::uninitialized_copy(rhs.begin(), rhs.end(), data_);
size_ = rhs.size();
}
fixed_capacity_vector& operator=(const fixed_capacity_vector& rhs) {
// left as an exercise for the reader
// also, move semantics left as an exercise for the reader
}
~fixed_capacity_vector() {
while (!empty()) pop_back();
}
};
Notice the Rule of Three/Five here. If you provide a custom destructor, you should also provide custom copy and move operations (or =delete them).
This is also available fully fleshed out in Boost, in the form of boost::static_vector; and there is an active WG21 proposal, P0843, currently targeting the Library Fundamentals v3 TS.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0843r4.html
HTH,
Arthur
Received on 2020-09-02 17:34:18