Iterator with STL Algorithms and std::execution::par Execution Policy - c++

How would one use an Iterator when specifying the std::execution::par Execution Policy for an STL algorithm such as std::for_each?
In the below example, I wish to take the square root of the diagonal of an Eigen3 Matrix as follows:
template<typename T>
using Matrix = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
template<typename T>
std::vector<T> getStdDev(const Matrix &x) const {
const size_t len = x.rows();
std::vector<T> stdDev(len);
// Generate indices.
size_t idx = 0;
std::vector<size_t> indices(len);
auto ind = [&idx]() {
return idx++;
};
std::generate(indices.begin(), indices.end(), ind);
// Take square root of diagonal elements.
auto sr = [&x](size_t i) {
stdDev[i] = std::sqrt(x(i, i));
};
std::for_each(std::execution::par, indices.begin(), indices.end(), sr);
return stdDev;
}
As far as I am aware, the above code is thread safe. However, how would one achieve the same effect with an Iterator in a thread safe manner without generating the desired indices first, as above? The assumption is that the container to be iterated over does not have an Iterator implemented, or does not have an Iterator that is thread safe.
Ideally, I wish to do this generically (I am aware Eigen has Iterators, it is used here just for example purposes).
Additionally, it would be highly preferable to only use C++ features (no libraries, but any non draft standard).

You can use something like boost::counting_iterator or ranges::view::iota
template<typename T>
std::vector<T> getStdDev(const Matrix &x) const {
const size_t len = x.rows();
std::vector<T> stdDev(len);
// Take square root of diagonal elements.
auto sr = [&x](size_t i) {
return std::sqrt(x(i, i));
};
std::transform(std::execution::par, boost::counting_iterator<int>(0), boost::counting_iterator<int>(len), stdDev.begin(), sr);
return stdDev;
}
Implementing a counting_iterator yourself isn't hard, it's just tedious to specify all the required members of RandomAccessIterator.
class counting_iterator
{
public:
typedef size_t value_type;
typedef const size_t& reference;
typedef const size_t* pointer;
typedef ptrdiff_t difference_type;
typedef random_access_iterator_tag iterator_category;
explicit counting_iterator(size_t x);
size_t const& base() const { return m_inc; }
reference operator*() const { return m_inc; }
counting_iterator& operator++() { ++m_inc; return *this; }
counting_iterator& operator--() { --m_inc; return *this; }
counting_iterator& operator+=(size_t i) { m_inc += i; return *this; }
counting_iterator& operator-=(size_t i) { m_inc -= i; return *this; }
// and loads of others
private:
size_t m_inc;
};

Related

const, span, and iterator trouble

I try to write an iterator that iterates over a container by index.
A It and a const It both allow changing the content of the container.
A Const_it and a const Const_it both forbid changing the content of the container.
After that, I try to write a span<T> over a container.
For a type T that is not const, both const span<T> and span<T> allows changing the content of the container.
Both const span<const T> and span<const T> forbid changing the content of the container.
The code does not compile because:
// *this is const within a const method
// But It<self_type> requires a non-const *this here.
// So the code does not compile
It<self_type> begin() const { return It<self_type>(*this, 0); }
If I make the constructor of It to accept a const container, it doesn't look right because the iterator can modify the content of the container.
If I get rid of the const of the method, then for a non-const type T, a const span<T> cannot modify the container.
It inherits from Const_it to allow implicit conversion from It to Const_it during template instantiation.
I use a pointer instead of a reference to in the iterators (const C* container_;) for allowing assigning one iterator to another iterator.
I suspect something is very wrong here because I even think about:
Does cast away const of *this cause undefined behavior?
But I don't know how to fix it.
Test:
#include <vector>
#include <numeric>
#include <iostream>
template<typename C>
class Const_it {
typedef Const_it<C> self_type;
public:
Const_it(const C& container, const int ix)
: container_(&container), ix_(ix) {}
self_type& operator++() {
++ix_;
return *this;
}
const int& operator*() const {
return ref_a()[ix_];
}
bool operator!=(const self_type& rhs) const {
return ix_ != rhs.ix_;
}
protected:
const C& ref_a() const { return *container_; }
const C* container_;
int ix_;
};
template<typename C>
class It : public Const_it<C> {
typedef Const_it<C> Base;
typedef It<C> self_type;
public:
//It(const C& container.
It(C& container, const int ix)
: Base::Const_it(container, ix) {}
self_type& operator++() {
++ix_;
return *this;
}
int& operator*() const {
return mutable_a()[ix_];
}
private:
C& mutable_a() const { return const_cast<C&>(ref_a()); }
using Base::ref_a;
using Base::container_;
using Base::ix_;
};
template <typename V>
class span {
typedef span<V> self_type;
public:
explicit span(V& v) : v_(v) {}
It<self_type> begin() { return It<self_type>(*this, 0); }
// *this is const within a const method
// But It<self_type> requires a non-const *this here.
// So the code does not compile
It<self_type> begin() const { return It<self_type>(*this, 0); }
It<self_type> end() { return It<self_type>(*this, v_.size()); }
It<self_type> end() const { return It<self_type>(*this, v_.size()); }
int& operator[](const int ix) {return v_[ix];}
const int& operator[](const int ix) const {return v_[ix];}
private:
V& v_;
};
int main() {
typedef std::vector<int> V;
V v(10);
std::iota(v.begin(), v.end(), 0);
std::cout << v.size() << "\n";
const span<V> s(v);
for (auto&& x : s) {
x = 4;
std::cout << x << "\n";
}
}
There are two main notes to be said to make this work. First:
If I make the constructor of It to accept a const container, it doesn't look right because the iterator can modify the content of the container.
Not really, because C in your template<typename C> class It is not the actual container, but the span<V>. In other words, take a look at:
It<self_type> begin() const { return It<self_type>(*this, 0); }
Here self_type means const span<V>, therefore you are returning a It<const span<V>>. Thus, your iterator can do whatever can be done with a const span -- but the container is still non-const. The variable name container_ is not fortunate, then.
For a type T that is not const, both const span<T> and span<T> allows changing the content of the container. Both const span<const T> and span<const T> forbid changing the content of the container.
In addition, since you want that const span is allowed to modify the contents, then what you should write inside span itself is (note the const):
int& operator[](const int ix) const {return v_[ix];}
// Removing the other `const` version:
// const int& operator[](const int ix) const {return v_[ix];}
With those two bits clarified, you can then construct a working example. Here is one based from your code and simplified to solve the issue at hand:
#include <vector>
#include <iostream>
template<typename S>
class It {
typedef It<S> self_type;
const S& span_;
int ix_;
public:
It(const S& span, const int ix)
: span_(span), ix_(ix) {}
self_type& operator++() {
++ix_;
return *this;
}
int& operator*() const {
return span_[ix_];
}
bool operator!=(const self_type& rhs) const {
return &span_ != &rhs.span_ or ix_ != rhs.ix_;
}
};
template <typename V>
class span {
typedef span<V> self_type;
public:
explicit span(V& v) : v_(v) {}
It<self_type> begin() const { return It<self_type>(*this, 0); }
It<self_type> end() const { return It<self_type>(*this, v_.size()); }
int& operator[](const int ix) const {return v_[ix];}
private:
V& v_;
};
int main() {
typedef std::vector<int> V;
V v(10);
const span<V> s(v);
for (auto&& x : s) {
x = 4;
std::cout << x << "\n";
}
}
Take a look as well at the corrected implementation of operator!= and at the fact that there is no need for the non-const version of begin() and end(). You can also throw there a cbegin() and cend(). Then you have to work on adding back the const iterator cases.
By the way, in case it saves some confusion for anybody: in the near future, std::span (proposed for C++20) might be added; and it will be just a (pointer-to-first-element, index) pair -- rather than your (pointer-to-container, index) version.
In other words, as its template parameter, it will take the elements' type, rather than a container:
span<std::vector<int>> s(v);
// vs
std::span<int> s(v);
This allows consumers of std::span to avoid knowing about which container is behind the scenes (or even no container: a contiguous memory area or an array).
Finally, you may want to take a look at GSL's implementation of std::span to get some inspiration on how to fully implement it (including the second template parameter about the extent).
After studying Acorn's solution, I found another fix.
This allows using the same iterator template for both a std::vector<T> and a span.
The two cases are different.
For a non-const type T, a const std::vector<T> forbids changing its element. A const span<T> allows changing its elements.
The main difference is It<const self_type>(*this, 0); in the span class and S& span_; instead of const S& span_; in the It class.
Fix:
#include <vector>
#include <iostream>
template<typename S>
class It {
typedef It<S> self_type;
public:
It(S& span, const int ix)
: span_(span), ix_(ix) {}
self_type& operator++() {
++ix_;
return *this;
}
int& operator*() const {
return span_[ix_];
}
bool operator!=(const self_type& rhs) const {
return &span_ != &rhs.span_ or ix_ != rhs.ix_;
}
private:
S& span_;
int ix_;
};
template<typename V>
class span {
typedef span<V> self_type;
public:
explicit span(V& v) : v_(v) {}
It<const self_type> begin() const {
return It<const self_type>(*this, 0);
}
It<const self_type> end() const {
return It<const self_type>(*this, v_.size());
}
int& operator[](const int ix) const { return v_[ix]; }
private:
V& v_;
};
int main() {
// Test adding iterator to a span
typedef std::vector<int> V;
V v(10);
const span<V> s(v);
for (auto&& x : s) {
x = 4;
std::cout << x << " ";
}
std::cout << "\n";
// Test adding iterator to a std::vector
const It<V> begin(v, 0);
const It<V> end(v, v.size());
for (auto it = begin; it != end; ++it) {
*it = 10;
std::cout << *it << " ";
}
std::cout << "\n";
}

c++ sorted view of range - how to create const_iterator?

I am trying to write a class that should act as a sorted view on some underlying sequence of elements. So far I have come up with a non-const version. Now I have problems adapting it to also provide const_iterator functionality.
The code I have so far looks like this:
// forward declare iterator
template <class InputIt>
class sorted_range_iter;
template <class InputIt>
class sorted_range {
friend class sorted_range_iter<InputIt>;
private:
using T = typename InputIt::value_type;
InputIt _first;
InputIt _last;
std::vector<size_t> _indices;
public:
using iterator = sorted_range_iter<InputIt>;
sorted_range() = default;
sorted_range(InputIt first, InputIt last)
: _first(first), _last(last), _indices(std::distance(_first, _last)) {
std::iota(_indices.begin(), _indices.end(), 0);
};
template <class Compare = std::less<T>>
void sort(Compare comp = Compare()) {
std::sort(_indices.begin(), _indices.end(),
[this, &comp](size_t i1, size_t i2) {
return comp(*(_first + i1), *(_first + i2));
});
}
size_t size() const { return _indices.size(); }
T& operator[](size_t pos) { return *(_first + _indices[pos]); }
const T& operator[](size_t pos) const { return (*this)[pos]; }
iterator begin() { return iterator(0, this); }
iterator end() { return iterator(size(), this); }
};
And the corresponding iterator looks like this:
template <class InputIt>
class sorted_range_iter
: public std::iterator<std::forward_iterator_tag, InputIt> {
friend class sorted_range<InputIt>;
private:
using T = typename InputIt::value_type;
size_t _index;
sorted_range<InputIt>* _range;
sorted_range_iter(size_t index, sorted_range<InputIt>* range)
: _index(index), _range(range) {}
public:
T& operator*() { return *(_range->_first + _range->_indices[_index]); }
// pre-increment
const sorted_range_iter<InputIt>& operator++() {
_index++;
return *this;
}
// post-increment
sorted_range_iter<InputIt> operator++(int) {
sorted_range_iter<InputIt> result = *this;
++(*this);
return result;
}
bool operator!=(const sorted_range_iter<InputIt>& other) const {
return _index != other._index;
}
};
An usage example looks like this:
std::vector<int> t{5, 2, 3, 4};
auto rit = ref.begin();
sorted_range<std::vector<int>::iterator> r(begin(t), end(t));
r.sort();
for(auto& x : r)
{
std::cout << x << std::endl;
}
Output:
2
3
4
5
How do I adapt my iterator for the const case? It would be easier if the iterator would be templated on the underlying type (int for example) instead InputIt. Is there a better way to define this class?
I suppose one could solve this for example by using the range-v3 library, however I am trying to not add any more dependencies and rely on C++11/14 functions.
You just are using the wrong type for T. You have:
using T = typename InputIt::value_type;
But value_type is the same for iterator and const_iterator. What they have are different reference types. You should prefer:
using R = typename std::iterator_traits<InputIt>::reference;
R operator*() { ... }
This has the added benefit of working with pointers.
Alternatively, could avoid iterator_traits by just trying to dereference the iterator itself:
using R = decltype(*std::declval<InputIt>());
Side-note, shouldn't sorted_range sort itself on construction? Otherwise, easy to misuse.

Sorting a pair of vectors

I know how to sort a vector of pairs, but how do you sort a pair of vectors? I can think of writing a custom "virtual" iterator over a pair of vectors and sorting that, but that seems quite complex. Is there an easier way? Is there one in C++03? I would like to use std::sort.
This problem arises when processing some data generated in hardware, where a pair of arrays makes more sense than array of pairs (since then there would be all kinds of stride and alignment problems). I realize that otherwise keeping a pair of vector instead of a vector of pairs would be a design flaw (the structure of arrays problem). I'm looking for a fast solution, copying the data to a vector of pairs and then back (I will return it to the HW to do more processing) is not an option.
Example:
keys = {5, 2, 3, 1, 4}
values = {a, b, d, e, c}
and after sorting (by the first vector):
keys = {1, 2, 3, 4, 5}
values = {e, b, d, c, a}
I refer to a "pair of vectors" as the pair of keys and values (stored as e.g. std::pair<std::vector<size_t>, std::vector<double> >). The vectors have the same length.
Let's make a sort/permute iterator, so that we can just say:
int keys[] = { 5, 2, 3, 1, 4 };
char vals[] = { 'a', 'b', 'd', 'e', 'c' };
std::sort(make_dual_iter(begin(keys), begin(vals)),
make_dual_iter(end(keys), end(vals)));
// output
std::copy(begin(keys), end(keys), std::ostream_iterator<int> (std::cout << "\nKeys:\t", "\t"));
std::copy(begin(vals), end(vals), std::ostream_iterator<char>(std::cout << "\nValues:\t", "\t"));
See it Live On Coliru, printing
Keys: 1 2 3 4 5
Values: e b d c a
Based on the idea here, I've implemented this:
namespace detail {
template <class KI, class VI> struct helper {
using value_type = boost::tuple<typename std::iterator_traits<KI>::value_type, typename std::iterator_traits<VI>::value_type>;
using ref_type = boost::tuple<typename std::iterator_traits<KI>::reference, typename std::iterator_traits<VI>::reference>;
using difference_type = typename std::iterator_traits<KI>::difference_type;
};
}
template <typename KI, typename VI, typename H = typename detail::helper<KI, VI> >
class dual_iter : public boost::iterator_facade<dual_iter<KI, VI>, // CRTP
typename H::value_type, std::random_access_iterator_tag, typename H::ref_type, typename H::difference_type>
{
public:
dual_iter() = default;
dual_iter(KI ki, VI vi) : _ki(ki), _vi(vi) { }
KI _ki;
VI _vi;
private:
friend class boost::iterator_core_access;
void increment() { ++_ki; ++_vi; }
void decrement() { --_ki; --_vi; }
bool equal(dual_iter const& other) const { return (_ki == other._ki); }
typename detail::helper<KI, VI>::ref_type dereference() const {
return (typename detail::helper<KI, VI>::ref_type(*_ki, *_vi));
}
void advance(typename H::difference_type n) { _ki += n; _vi += n; }
typename H::difference_type distance_to(dual_iter const& other) const { return ( other._ki - _ki); }
};
Now the factory function is simply:
template <class KI, class VI>
dual_iter<KI, VI> make_dual_iter(KI ki, VI vi) { return {ki, vi}; }
Note I've been a little lazy by using boost/tuples/tuple_comparison.hpp for the sorting. This could pose a problem with stable sort when multiple key values share the same value. However, in this case it's hard to define what is "stable" sort anyways, so I didn't think it important for now.
FULL LISTING
Live On Coliru
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/tuple/tuple_comparison.hpp>
namespace boost { namespace tuples {
// MSVC might not require this
template <typename T, typename U>
inline void swap(boost::tuple<T&, U&> a, boost::tuple<T&, U&> b) noexcept {
using std::swap;
swap(boost::get<0>(a), boost::get<0>(b));
swap(boost::get<1>(a), boost::get<1>(b));
}
} }
namespace detail {
template <class KI, class VI> struct helper {
using value_type = boost::tuple<typename std::iterator_traits<KI>::value_type, typename std::iterator_traits<VI>::value_type>;
using ref_type = boost::tuple<typename std::iterator_traits<KI>::reference, typename std::iterator_traits<VI>::reference>;
using difference_type = typename std::iterator_traits<KI>::difference_type;
};
}
template <typename KI, typename VI, typename H = typename detail::helper<KI, VI> >
class dual_iter : public boost::iterator_facade<dual_iter<KI, VI>, // CRTP
typename H::value_type, std::random_access_iterator_tag, typename H::ref_type, typename H::difference_type>
{
public:
dual_iter() = default;
dual_iter(KI ki, VI vi) : _ki(ki), _vi(vi) { }
KI _ki;
VI _vi;
private:
friend class boost::iterator_core_access;
void increment() { ++_ki; ++_vi; }
void decrement() { --_ki; --_vi; }
bool equal(dual_iter const& other) const { return (_ki == other._ki); }
typename detail::helper<KI, VI>::ref_type dereference() const {
return (typename detail::helper<KI, VI>::ref_type(*_ki, *_vi));
}
void advance(typename H::difference_type n) { _ki += n; _vi += n; }
typename H::difference_type distance_to(dual_iter const& other) const { return ( other._ki - _ki); }
};
template <class KI, class VI>
dual_iter<KI, VI> make_dual_iter(KI ki, VI vi) { return {ki, vi}; }
#include <iostream>
using std::begin;
using std::end;
int main()
{
int keys[] = { 5, 2, 3, 1, 4 };
char vals[] = { 'a', 'b', 'd', 'e', 'c' };
std::sort(make_dual_iter(begin(keys), begin(vals)),
make_dual_iter(end(keys), end(vals)));
std::copy(begin(keys), end(keys), std::ostream_iterator<int> (std::cout << "\nKeys:\t", "\t"));
std::copy(begin(vals), end(vals), std::ostream_iterator<char>(std::cout << "\nValues:\t", "\t"));
}
Just for comparison, this is how much code the split iterator approach requires:
template <class V0, class V1>
class CRefPair { // overrides copy semantics of std::pair
protected:
V0 &m_v0;
V1 &m_v1;
public:
CRefPair(V0 &v0, V1 &v1)
:m_v0(v0), m_v1(v1)
{}
void swap(CRefPair &other)
{
std::swap(m_v0, other.m_v0);
std::swap(m_v1, other.m_v1);
}
operator std::pair<V0, V1>() const // both g++ and msvc sort requires this (to get a pivot)
{
return std::pair<V0, V1>(m_v0, m_v1);
}
CRefPair &operator =(std::pair<V0, V1> v) // both g++ and msvc sort requires this (for insertion sort)
{
m_v0 = v.first;
m_v1 = v.second;
return *this;
}
CRefPair &operator =(const CRefPair &other) // required by g++ (for _GLIBCXX_MOVE)
{
m_v0 = other.m_v0;
m_v1 = other.m_v1;
return *this;
}
};
template <class V0, class V1>
inline bool operator <(std::pair<V0, V1> a, CRefPair<V0, V1> b) // required by both g++ and msvc
{
return a < std::pair<V0, V1>(b); // default pairwise lexicographical comparison
}
template <class V0, class V1>
inline bool operator <(CRefPair<V0, V1> a, std::pair<V0, V1> b) // required by both g++ and msvc
{
return std::pair<V0, V1>(a) < b; // default pairwise lexicographical comparison
}
template <class V0, class V1>
inline bool operator <(CRefPair<V0, V1> a, CRefPair<V0, V1> b) // required by both g++ and msvc
{
return std::pair<V0, V1>(a) < std::pair<V0, V1>(b); // default pairwise lexicographical comparison
}
namespace std {
template <class V0, class V1>
inline void swap(CRefPair<V0, V1> &a, CRefPair<V0, V1> &b)
{
a.swap(b);
}
} // ~std
template <class It0, class It1>
class CPairIterator : public std::random_access_iterator_tag {
public:
typedef typename std::iterator_traits<It0>::value_type value_type0;
typedef typename std::iterator_traits<It1>::value_type value_type1;
typedef std::pair<value_type0, value_type1> value_type;
typedef typename std::iterator_traits<It0>::difference_type difference_type;
typedef /*typename std::iterator_traits<It0>::distance_type*/difference_type distance_type; // no distance_type in g++, only in msvc
typedef typename std::iterator_traits<It0>::iterator_category iterator_category;
typedef CRefPair<value_type0, value_type1> reference;
typedef reference *pointer; // not so sure about this, probably can't be implemented in a meaningful way, won't be able to overload ->
// keep the iterator traits happy
protected:
It0 m_it0;
It1 m_it1;
public:
CPairIterator(const CPairIterator &r_other)
:m_it0(r_other.m_it0), m_it1(r_other.m_it1)
{}
CPairIterator(It0 it0 = It0(), It1 it1 = It1())
:m_it0(it0), m_it1(it1)
{}
reference operator *()
{
return reference(*m_it0, *m_it1);
}
value_type operator *() const
{
return value_type(*m_it0, *m_it1);
}
difference_type operator -(const CPairIterator &other) const
{
assert(m_it0 - other.m_it0 == m_it1 - other.m_it1);
// the iterators always need to have the same position
// (incomplete check but the best we can do without having also begin / end in either vector)
return m_it0 - other.m_it0;
}
bool operator ==(const CPairIterator &other) const
{
assert(m_it0 - other.m_it0 == m_it1 - other.m_it1);
return m_it0 == other.m_it0;
}
bool operator !=(const CPairIterator &other) const
{
return !(*this == other);
}
bool operator <(const CPairIterator &other) const
{
assert(m_it0 - other.m_it0 == m_it1 - other.m_it1);
return m_it0 < other.m_it0;
}
bool operator >=(const CPairIterator &other) const
{
return !(*this < other);
}
bool operator <=(const CPairIterator &other) const
{
return !(other < *this);
}
bool operator >(const CPairIterator &other) const
{
return other < *this;
}
CPairIterator operator +(distance_type d) const
{
return CPairIterator(m_it0 + d, m_it1 + d);
}
CPairIterator operator -(distance_type d) const
{
return *this + -d;
}
CPairIterator &operator +=(distance_type d)
{
return *this = *this + d;
}
CPairIterator &operator -=(distance_type d)
{
return *this = *this + -d;
}
CPairIterator &operator ++()
{
return *this += 1;
}
CPairIterator &operator --()
{
return *this += -1;
}
CPairIterator operator ++(int) // msvc sort actually needs this, g++ does not
{
CPairIterator old = *this;
++ (*this);
return old;
}
CPairIterator operator --(int)
{
CPairIterator old = *this;
-- (*this);
return old;
}
};
template <class It0, class It1>
inline CPairIterator<It0, It1> make_pair_iterator(It0 it0, It1 it1)
{
return CPairIterator<It0, It1>(it0, it1);
}
It is kind of rough around the edges, maybe I'm just bad at overloading the comparisons, but the amount of differences needed to support different implementations of std::sort makes me think the hackish solution might actually be more portable. But the sorting is much nicer:
struct CompareByFirst {
bool operator ()(std::pair<size_t, char> a, std::pair<size_t, char> b) const
{
return a.first < b.first;
}
};
std::vector<char> vv; // filled by values
std::vector<size_t> kv; // filled by keys
std::sort(make_pair_iterator(kv.begin(), vv.begin()),
make_pair_iterator(kv.end(), vv.end()), CompareByFirst());
// nice
And of course it gives the correct result.
Inspired by a comment by Mark Ransom, this is a horrible hack, and an example of how not to do it. I only wrote it for amusement and because I was wondering how complicated would it get. This is not an answer to my question, I will not use this. I just wanted to share a bizarre idea. Please, do not downvote.
Actually, ignoring multithreading, I believe this could be done:
template <class KeyType, class ValueVectorType>
struct MyKeyWrapper { // all is public to save getters
KeyType k;
bool operator <(const MyKeyWrapper &other) const { return k < other.k; }
};
template <class KeyType, class ValueVectorType>
struct ValueVectorSingleton { // all is public to save getters, but kv and vv should be only accessible by getters
static std::vector<MyKeyWrapper<KeyType, ValueVectorType> > *kv;
static ValueVectorType *vv;
static void StartSort(std::vector<MyKeyWrapper<KeyType, ValueVectorType> > &_kv, ValueVectorType &_vv)
{
assert(!kv && !vv); // can't sort two at once (if multithreading)
assert(_kv.size() == _vv.size());
kv = &_kv, vv = &_vv; // not an attempt of an atomic operation
}
static void EndSort()
{
kv = 0, vv = 0; // not an attempt of an atomic operation
}
};
template <class KeyType, class ValueVectorType>
std::vector<MyKeyWrapper<KeyType, ValueVectorType> >
*ValueVectorSingleton<KeyType, ValueVectorType>::kv = 0;
template <class KeyType, class ValueVectorType>
ValueVectorType *ValueVectorSingleton<KeyType, ValueVectorType>::vv = 0;
namespace std {
template <class KeyType, class ValueVectorType>
void swap(MyKeyWrapper<KeyType, ValueVectorType> &a,
MyKeyWrapper<KeyType, ValueVectorType> &b)
{
assert((ValueVectorSingleton<KeyType, ValueVectorType>::vv &&
ValueVectorSingleton<KeyType, ValueVectorType>::kv)); // if this triggers, someone forgot to call StartSort()
ValueVectorType &vv = *ValueVectorSingleton<KeyType, ValueVectorType>::vv;
std::vector<MyKeyWrapper<KeyType, ValueVectorType> > &kv =
*ValueVectorSingleton<KeyType, ValueVectorType>::kv;
size_t ai = &kv.front() - &a, bi = &kv.front() - &b; // get indices in key vector
std::swap(a, b); // swap keys
std::swap(vv[ai], vv[bi]); // and any associated values
}
} // ~std
And sorting as:
std::vector<char> vv; // filled by values
std::vector<MyKeyWrapper<size_t, std::vector<char> > > kv; // filled by keys, casted to MyKeyWrapper
ValueVectorSingleton<size_t, std::vector<char> >::StartSort(kv, vv);
std::sort(kv.begin(), kv.end());
ValueVectorSingleton<size_t, std::vector<char> >::EndSort();
// trick std::sort into using the custom std::swap which also swaps the other vectors
This is obviously very appalling, trivial to abuse in horrible ways, but arguably much shorter than the pair of iterators and probably similar in performance. And it actually works.
Note that swap() could be implemented inside ValueVectorSingleton and the one injected in the std namespace would just call it. That would avoid having to make vv and kv public. Also, the addresses of a and b could further be checked to make sure they are inside kv and not some other vector. Also, this is limited to sorting by values of only one vector (can't sort by corresponding values in both vectors at the same time). And the template parameters could be simply KeyType and ValueType, this was written in a hurry.
Here is a solution I once used to sort an array together with an array of indices (--maybe it is from somewhere over here?):
template <class iterator>
class IndexComparison
{
public:
IndexComparison (iterator const& _begin, iterator const& _end) :
begin (_begin),
end (_end)
{}
bool operator()(size_t a, size_t b) const
{
return *std::next(begin,a) < *std::next(begin,b);
}
private:
const iterator begin;
const iterator end;
};
Usage:
std::vector<int> values{5,2,5,1,9};
std::vector<size_t> indices(values.size());
std::iota(indices.begin(),indices.end(),0);
std::sort(indices.begin(),indices.end()
, IndexComparison<decltype(values.cbegin())>(values.cbegin(),values.cend()));
Afterwards, the integers in vector indices are permuted such that they correspond to increasing values in the vector values. It is easy to extend this from less-comparison to general comparison functions.
Next, in order to sort also the values, you can do another
std::sort(values.begin(),values.end());
using the same comparison function. This is the solution for the lazy ones. Of course, you can alternatively also use the sorted indices according to
auto temp=values;
for(size_t i=0;i<indices.size();++i)
{
values[i]=temp[indices[i]];
}
DEMO
EDIT: I just realized that the above sorts into the opposite direction than the one you were asking for.

How to stable_sort without copying?

Why does stable_sort need a copy constructor? (swap should suffice, right?)
Or rather, how do I stable_sort a range without copying any elements?
#include <algorithm>
class Person
{
Person(Person const &); // Disable copying
public:
Person() : age(0) { }
int age;
void swap(Person &other) { using std::swap; swap(this->age, other.age); }
friend void swap(Person &a, Person &b) { a.swap(b); }
bool operator <(Person const &other) const { return this->age < other.age; }
};
int main()
{
static size_t const n = 10;
Person people[n];
std::stable_sort(people, people + n);
}
Expanding upon the discussion in the OP, and because I found it interesting, here's a solution which uses only swap to sort the original vector (by using a pointer wrapper to sort indices).
Edit: this is the solution v2, which swaps in-place.
Edit (by OP): An STL-friendly version which doesn't require C++11.
template<class Pred>
struct swapping_stable_sort_pred
{
Pred pred;
swapping_stable_sort_pred(Pred const &pred) : pred(pred) { }
template<class It>
bool operator()(
std::pair<It, typename std::iterator_traits<It>::difference_type> const &a,
std::pair<It, typename std::iterator_traits<It>::difference_type> const &b) const
{
bool less = this->pred(*a.first, *b.first);
if (!less)
{
bool const greater = this->pred(*b.first, *a.first);
if (!greater) { less = a.second < b.second; }
}
return less;
}
};
template<class It, class Pred>
void swapping_stable_sort(It const begin, It const end, Pred const pred)
{
typedef std::pair<It, typename std::iterator_traits<It>::difference_type> Pair;
std::vector<Pair> vp;
vp.reserve(static_cast<size_t>(std::distance(begin, end)));
for (It it = begin; it != end; ++it)
{ vp.push_back(std::make_pair(it, std::distance(begin, it))); }
std::sort(vp.begin(), vp.end(), swapping_stable_sort_pred<Pred>(pred));
std::vector<Pair *> vip(vp.size());
for (size_t i = 0; i < vp.size(); i++)
{ vip[static_cast<size_t>(vp[i].second)] = &vp[i]; }
for (size_t i = 0; i + 1 < vp.size(); i++)
{
typename std::iterator_traits<It>::difference_type &j = vp[i].second;
using std::swap;
swap(*(begin + static_cast<ptrdiff_t>(i)), *(begin + j));
swap(j, vip[i]->second);
swap(vip[j], vip[vip[j]->second]);
}
}
template<class It>
void swapping_stable_sort(It const begin, It const end)
{ return swapping_stable_sort(begin, end, std::less<typename std::iterator_traits<It>::value_type>()); }
I don't own a copy of the standard. For what it's worth, this is the wording from a freely available 2010 draft:
25.4.1.2 stable_sort
[...]
Requires: The type of *first shall satisfy the Swappable requirements (Table 37), the MoveConstructible
requirements (Table 33), and the the MoveAssignable requirements (Table 35).
Testing with the latest Visual C++, it does allow sorting when a move constructor is defined but the copy constructor is private.
So to answer your question: you're out of luck. Use something other than std::stable_sort or use a wrapper class.

boost::aligned_storage copying NON-POD objects like a POD type on return

I have a class that is using boost::aligned storage to statically allocate memory within the object, then initialize the objects at a later time. However, a crash in one test program appears to show the members' destructors being called on the initialized object, a std::set in this case, when I attempt to return the object after a function call. Then, only the data appears to be copied over on assignment to the new object at the return site, as if the object was a POD type. This means that the next time I attempt to access the assigned object, it fails with a memory access error.
More specifically, I'm implementing a class called StaticVector, a subset of std::vector which attempts to closely match Boost.Array plus size tracking and a compile time fixed capacity.
My test program benchStaticVector runs as follows:
calls a function that adds a randomly filled std::set to a StaticVector
returns it to a local variable
the destructor is being called on the return, but for some reason the returned object isn't copying correctly
when the local variable goes out of scope free gets called on uninitialized memory
Crash
What is the source of this crash, and how do I resolve it?
The full code and a convenient CMakeLists.txt file can be found at:
https://github.com/ahundt/Boost.StaticVector
benchStaticVector.cpp:
// benchmark based on: http://cpp-next.com/archive/2010/10/howards-stl-move-semantics-benchmark/
#include "StaticVector.hpp"
#include <vector>
#include <iostream>
#include <time.h>
#include <set>
#include <algorithm>
#include <exception>
const unsigned N = 3;
extern bool some_test;
template<typename T>
T get_set(std::size_t)
{
T s;
for (std::size_t i = 0; i < N; ++i)
while (!s.insert(std::rand()).second)
;
if (some_test)
return s;
return T();
}
template<typename T>
T generate()
{
T v;
for (std::size_t i = 0; i < N; ++i)
v.push_back(get_set<typename T::value_type>(i));
if (some_test)
return v;
return T();
}
template<typename T>
float time_it()
{
clock_t t1, t2, t3, t4;
clock_t t0 = clock();
{
T v(generate<T>());
}
return (float)((t4-t0)/(double)CLOCKS_PER_SEC);
}
int main()
{
try {
std::cout << "N = " << N << "\n\n";
std::cout << "StaticVector Benchmark:\n";
float t = time_it<boost::StaticVector<std::set<std::size_t>,N > >();
std::cout << "Total time = " << t << "\n\n";
std::cout << "Vector: Benchmark\n";
t = time_it<std::vector<std::set<std::size_t> > >();
std::cout << "Total time = " << t << '\n';
}catch(std::exception e){
std::cout << e.what();
}
}
bool some_test = true;
Here is the class in StaticVector.hpp:
/**
* #file StaticVector.hpp
* #date Feb 23, 2011
* #brief Vector style class with fixed capacity.
*
* The following code declares class StaticVector,
* an STL container (as wrapper) for a statically allocated vector with a constant size limit.
* StaticVector is not accepted as part of boost.
*
* (C) Carnegie Mellon University 2011
* #author Andrew Hundt <ahundt#cmu.edu>
*
* based on boost::array
* The original author site of boost::array is at: http://www.josuttis.com/
* (C) Copyright Nicolai M. Josuttis 2001.
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* 09 Oct 2011 - (ath) eliminated construction of objects on initialization of StaticVector
* 23 Feb 2011 - (ath) converted to boost::StaticVector
* 28 Dec 2010 - (mtc) Added cbegin and cend (and crbegin and crend) for C++Ox compatibility.
* 10 Mar 2010 - (mtc) fill method added, matching resolution of the standard library working group.
* See <http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#776> or Trac issue #3168
* Eventually, we should remove "assign" which is now a synonym for "fill" (Marshall Clow)
* 10 Mar 2010 - added workaround for SUNCC and !STLPort [trac #3893] (Marshall Clow)
* 29 Jan 2004 - c_array() added, BOOST_NO_PRIVATE_IN_AGGREGATE removed (Nico Josuttis)
* 23 Aug 2002 - fix for Non-MSVC compilers combined with MSVC libraries.
* 05 Aug 2001 - minor update (Nico Josuttis)
* 20 Jan 2001 - STLport fix (Beman Dawes)
* 29 Sep 2000 - Initial Revision (Nico Josuttis)
*
* Jan 29, 2004
*/
#ifndef BOOST_STATIC_VECTOR_HPP
#define BOOST_STATIC_VECTOR_HPP
#include <boost/config.hpp>
#include <boost/detail/workaround.hpp>
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
# pragma warning(push)
# pragma warning(disable:4996) // 'std::equal': Function call with parameters that may be unsafe
# pragma warning(disable:4510) // boost::StaticVector<T,N>' : default constructor could not be generated
# pragma warning(disable:4610) // warning C4610: class 'boost::StaticVector<T,N>' can never be instantiated - user defined constructor required
#endif
#include <cstddef>
#include <stdexcept>
#include <boost/assert.hpp>
#if ((BOOST_VERSION / 100) % 1000) > 44
#include <boost/swap.hpp>
#endif
// Handles broken standard libraries better than <iterator>
#include <boost/detail/iterator.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
// FIXES for broken compilers
#include <boost/config.hpp>
// Selection of types for internal storage
#include <boost/integer.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/has_trivial_destructor.hpp>
namespace boost {
template<class T, std::size_t N>
class StaticVector {
public:
// type definitions
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef typename boost::aligned_storage<
sizeof(T),
boost::alignment_of<T>::value
>::type aligned_storage;
typedef typename boost::uint_value_t<N>::least size_type;
typedef std::size_t max_size_type;
typedef std::ptrdiff_t difference_type;
private:
size_type m_size; // fastest type that can accomodate N
aligned_storage elems[N]; // fixed-size array of memory aligned elements of type T
public:
// iterator support
iterator begin() { return reinterpret_cast<iterator>(elems); }
const_iterator begin() const { return reinterpret_cast<const_iterator>(elems); }
const_iterator cbegin() const { return reinterpret_cast<const_iterator>(elems); }
iterator end() { return to_object(m_size); }
const_iterator end() const { return to_object(m_size); }
const_iterator cend() const { return to_object(m_size); }
// reverse iterator support
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS)
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310)
// workaround for broken reverse_iterator in VC7
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, iterator,
reference, iterator, reference> > reverse_iterator;
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, const_iterator,
const_reference, iterator, reference> > const_reverse_iterator;
#elif defined(_RWSTD_NO_CLASS_PARTIAL_SPEC)
typedef std::reverse_iterator<iterator, std::random_access_iterator_tag,
value_type, reference, iterator, difference_type> reverse_iterator;
typedef std::reverse_iterator<const_iterator, std::random_access_iterator_tag,
value_type, const_reference, const_iterator, difference_type> const_reverse_iterator;
#else
// workaround for broken reverse_iterator implementations
typedef std::reverse_iterator<iterator,T> reverse_iterator;
typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
#endif
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator crbegin() const {
return const_reverse_iterator(end());
}
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
const_reverse_iterator crend() const {
return const_reverse_iterator(begin());
}
StaticVector():m_size(0){}
StaticVector(size_type n, const_reference value):
m_size(n)
{
insert(begin(),n,value);
}
template<typename InputIterator>
StaticVector(InputIterator first, InputIterator last):
m_size(last-first)
{
capacitycheck(size());
std::copy(first,last,begin());
}
template<std::size_t SizeRHS>
StaticVector(const StaticVector<T,SizeRHS>& rhs):
m_size(rhs.size())
{
capacitycheck(rhs.size());
std::copy(rhs.begin(),rhs.end(),begin());
}
~StaticVector(){
destroy_array(::boost::has_trivial_destructor<T>());
m_size=0;
}
void push_back (const_reference x){
capacitycheck(size()+1);
new (to_object(size())) T(x);
m_size++;
}
void pop_back(){
if(!empty()){
to_object(size()-1)->~T();
m_size--;
} else {
boost::throw_exception( std::out_of_range("StaticVector<> pop called on empty container."));
}
}
iterator insert(iterator pos, const_reference x){
capacitycheck(size()+1);
std::copy_backward(pos,end(),end()+1);
*pos = x;
m_size++;
return pos;
}
void insert(iterator pos, max_size_type n, const_reference x){
capacitycheck(size()+n);
std::copy_backward(pos,end(),end()+n);
std::fill(pos,pos+n,x);
m_size+=n;
}
template <typename InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last){
max_size_type n = last - first;
capacitycheck(size()+n);
std::copy_backward(pos,end(),end()+n);
std::copy(first,last,pos);
}
iterator erase(iterator pos){
rangecheck(pos-begin());
pos->~T();
std::copy(pos+1,end(),pos);
m_size--;
return pos;
}
iterator erase(iterator first, iterator last){
std::ptrdiff_t n = last-first;
if (n>0) {
rangecheck(size()-n);
for(iterator it = first; it!=last; it++){
it->~T();
}
std::copy(last,end(),first);
m_size -= n;
}
return first;
}
void clear(){
erase(begin(),end());
}
void resize(max_size_type n, const_reference t = T() ){
capacitycheck(n);
if(n - m_size > 0){
std::fill_n(end(), n-m_size, t);
} else {
erase(begin()+n,end());
}
m_size = n;
}
void reserve(max_size_type n){
capacitycheck(n);
}
// operator[]
reference operator[](max_size_type i)
{
BOOST_ASSERT( i < N || i < size() && "StaticVector<>: out of range" );
return *to_object(i);
}
const_reference operator[](max_size_type i) const
{
BOOST_ASSERT( i < N || i < size() && "StaticVector<>: out of range" );
return *to_object(i);
}
// at() with range check
reference at(max_size_type i) { rangecheck(i); return *to_object(i); }
const_reference at(max_size_type i) const { rangecheck(i); return *to_object(i); }
// front() and back()
reference front()
{
return begin();
}
const_reference front() const
{
return begin();
}
reference back()
{
return *to_object(size()-1);
}
const_reference back() const
{
return *to_object(size()-1);
}
// capacity is constant, size varies
max_size_type size() const { return m_size; }
static max_size_type capacity() { return N; }
bool empty() { return m_size == 0; }
static max_size_type max_size() { return N; }
enum { static_size = N };
// swap (note: linear complexity)
void swap (StaticVector<T,N>& y) {
#if ((BOOST_VERSION / 100) % 1000) > 44
for (size_type i = 0; i < N; ++i)
boost::swap(*to_object(i),*y.to_object(i));
boost::swap(m_size,y.m_size);
#else
std::swap_ranges(begin(),end(),y.begin());
std::swap(m_size,y.m_size);
#endif
}
// direct access to data (read-only)
const_pointer data() const { return elems; }
pointer data() { return elems; }
// use array as C array (direct read/write access to data)
pointer c_array() { return elems; }
// assignment with type conversion
template <typename T2>
StaticVector<T,N>& operator= (const StaticVector<T2,N>& rhs) {
std::copy(rhs.begin(),rhs.end(), begin());
m_size = rhs.size();
return *this;
}
// assign one value to all elements
void assign (const T& value) { fill ( value ); } // A synonym for fill
void fill (const T& value)
{
std::fill_n(begin(),size(),value);
}
// check range (may be private because it is static)
void rangecheck (max_size_type i) const {
if (i >= size()) {
std::out_of_range e("StaticVector<>: index out of range");
boost::throw_exception(e);
}
}
// check range (may be private because it is static)
void capacitycheck (max_size_type i) const {
if (i > capacity()) {
std::out_of_range e("StaticVector<>: index out of capacity");
boost::throw_exception(e);
}
}
private:
inline const_pointer to_object(size_type index) const {
return reinterpret_cast<const_pointer>(elems+index);
}
inline pointer to_object(size_type index) {
return reinterpret_cast<pointer>(elems+index);
}
// T has a trivial destructor, do nothing
inline void destroy_array(const boost::true_type&) {}
// T has a destructor, destroy each object
inline void destroy_array(const boost::false_type&) {
for(iterator first = begin(); first != end(); ++first) {
first->~T();
}
}
}; // class StaticVector
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
template< class T >
class StaticVector< T, 0 > {
public:
// type definitions
typedef T value_type;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef typename boost::aligned_storage<
sizeof(T),
boost::alignment_of<T>::value
>::type aligned_storage;
typedef typename boost::uint_value_t<0>::least size_type;
typedef std::size_t max_size_type;
typedef std::ptrdiff_t difference_type;
// iterator support
iterator begin() { return iterator( reinterpret_cast< T * >( this ) ); }
const_iterator begin() const { return const_iterator( reinterpret_cast< const T * >( this ) ); }
const_iterator cbegin() const { return const_iterator( reinterpret_cast< const T * >( this ) ); }
iterator end() { return begin(); }
const_iterator end() const { return begin(); }
const_iterator cend() const { return cbegin(); }
// reverse iterator support
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS)
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310)
// workaround for broken reverse_iterator in VC7
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, iterator,
reference, iterator, reference> > reverse_iterator;
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, const_iterator,
const_reference, iterator, reference> > const_reverse_iterator;
#elif defined(_RWSTD_NO_CLASS_PARTIAL_SPEC)
typedef std::reverse_iterator<iterator, std::random_access_iterator_tag,
value_type, reference, iterator, difference_type> reverse_iterator;
typedef std::reverse_iterator<const_iterator, std::random_access_iterator_tag,
value_type, const_reference, const_iterator, difference_type> const_reverse_iterator;
#else
// workaround for broken reverse_iterator implementations
typedef std::reverse_iterator<iterator,T> reverse_iterator;
typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
#endif
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator crbegin() const {
return const_reverse_iterator(end());
}
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
const_reverse_iterator crend() const {
return const_reverse_iterator(begin());
}
void push_back (const_reference x){
failed_rangecheck();
}
void pop_back(){
failed_rangecheck();
}
iterator insert(iterator pos, const_reference x){
return failed_rangecheck();
}
void insert(iterator pos, max_size_type n, const_reference x){
failed_rangecheck();
}
template <typename InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last){
failed_rangecheck();
}
iterator erase(iterator pos){
return failed_rangecheck();
}
iterator erase(iterator first, iterator last){
return failed_rangecheck();
}
void clear(){
}
void resize(max_size_type n, const_reference t = T() ){
failed_rangecheck();
}
void reserve(size_type n){
failed_rangecheck();
}
// operator[]
reference operator[](max_size_type /*i*/)
{
return failed_rangecheck();
}
const_reference operator[](max_size_type /*i*/) const
{
return failed_rangecheck();
}
// at() with range check
reference at(max_size_type /*i*/) { return failed_rangecheck(); }
const_reference at(max_size_type /*i*/) const { return failed_rangecheck(); }
// front() and back()
reference front()
{
return failed_rangecheck();
}
const_reference front() const
{
return failed_rangecheck();
}
reference back()
{
return failed_rangecheck();
}
const_reference back() const
{
return failed_rangecheck();
}
// size is constant
static max_size_type size() { return 0; }
static bool empty() { return true; }
static max_size_type max_size() { return 0; }
enum { static_size = 0 };
void swap (StaticVector<T,0>& /*y*/) {
}
// direct access to data (read-only)
const T* data() const { return 0; }
T* data() { return 0; }
// use array as C array (direct read/write access to data)
T* c_array() { return 0; }
// assignment with type conversion
template <typename T2>
StaticVector<T,0>& operator= (const StaticVector<T2,0>& ) {
return *this;
}
// assign one value to all elements
void assign (const T& value) { fill ( value ); }
void fill (const T& ) {}
// check range (may be private because it is static)
static reference failed_rangecheck () {
std::out_of_range e("attempt to access element of an empty StaticVector");
boost::throw_exception(e);
#if defined(BOOST_NO_EXCEPTIONS) || !defined(BOOST_MSVC)
//
// We need to return something here to keep
// some compilers happy: however we will never
// actually get here....
//
static T placeholder;
return placeholder;
#endif
}
};
#endif
// comparisons
template<class T, std::size_t N>
bool operator== (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return std::equal(x.begin(), x.end(), y.begin());
}
template<class T, std::size_t N>
bool operator< (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end());
}
template<class T, std::size_t N>
bool operator!= (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return !(x==y);
}
template<class T, std::size_t N>
bool operator> (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return y<x;
}
template<class T, std::size_t N>
bool operator<= (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return !(y<x);
}
template<class T, std::size_t N>
bool operator>= (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return !(x<y);
}
// global swap()
template<class T, std::size_t N>
inline void swap (StaticVector<T,N>& x, StaticVector<T,N>& y) {
x.swap(y);
}
#if defined(__SUNPRO_CC)
// Trac ticket #4757; the Sun Solaris compiler can't handle
// syntax like 'T(&get_c_array(boost::StaticVector<T,N>& arg))[N]'
//
// We can't just use this for all compilers, because the
// borland compilers can't handle this form.
namespace detail {
template <typename T, std::size_t N> struct c_array
{
typedef T type[N];
};
}
// Specific for boost::StaticVector: simply returns its elems data member.
template <typename T, std::size_t N>
typename detail::c_array<T,N>::type& get_c_array(boost::StaticVector<T,N>& arg)
{
return arg.elems;
}
// Specific for boost::StaticVector: simply returns its elems data member.
template <typename T, std::size_t N>
typename const detail::c_array<T,N>::type& get_c_array(const boost::StaticVector<T,N>& arg)
{
return arg.elems;
}
#else
// Specific for boost::StaticVector: simply returns its elems data member.
template <typename T, std::size_t N>
T(&get_c_array(boost::StaticVector<T,N>& arg))[N]
{
return arg.elems;
}
// Const version.
template <typename T, std::size_t N>
const T(&get_c_array(const boost::StaticVector<T,N>& arg))[N]
{
return arg.elems;
}
#endif
#if 0
// Overload for std::array, assuming that std::array will have
// explicit conversion functions as discussed at the WG21 meeting
// in Summit, March 2009.
template <typename T, std::size_t N>
T(&get_c_array(std::array<T,N>& arg))[N]
{
return static_cast<T(&)[N]>(arg);
}
// Const version.
template <typename T, std::size_t N>
const T(&get_c_array(const std::array<T,N>& arg))[N]
{
return static_cast<T(&)[N]>(arg);
}
#endif
} /* namespace boost */
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
# pragma warning(pop)
#endif
#endif /*BOOST_STATIC_VECTOR_HPP*/
You broke the rule of three. You need a copy constructor. Note that your constructor taking const StaticVector<T,SizeRHS>& is not a copy constructor as it is a template. Hence when a StaticVector is copied (e.g. possibly when returned from a function) the compiler generated copy constructor is used, copying the storage (that's okay, it's a POD). At destruction time, the destructor accesses the storage as if it contained live objects, and that's terrible.
boost::aligned_storage is in fact a POD type (required by C++11), and has no knowledge of the type stored within. It just provides 'storage'. If you want to copy objects from one storage to another, you will have to perform the copy yourself.
Have you considered implementing your own allocator based on stack storage instead, and reuse the standard containers?