In general std::vector object size is 24 bytes, as it is implemented as 3 pointers (each pointer is 8 bytes in size on 64-bit CPUs). These pointers are:
begin of vector
end of vector
end of reserved memory for vector (vector capacity)
Do we have any similar container that offers same interface (except for the capacity feature) but is implemented only with two pointers (or perhaps a pointer + size value)?
It makes sense in some cases like my current project. I hold millions of vectors in memory, but I do not need the distinction between vector size and capacity, and memory usage is turning a bottleneck. I have considered a couple of options:
std::string implementation may be tricky and include things like short string optimization, which may be even worse.
std::unique_ptr to some allocated buffer. The interface is not so convenient as std::vector, and I would end up creating my own class that wraps the pointer plus size of the buffer, which is what I am trying to avoid.
So any ready made alternative? Boost libraries based solutions are accepted.
Here's the absolute minimum to encapsulate a unique_ptr<T[]> in a first-class value-type that models a RandomAccessRange:
#include <memory>
template <typename T>
struct dyn_array {
explicit dyn_array(size_t n)
: _n(n), _data(std::make_unique<T[]>(n)) { }
auto begin() const { return _data.get(); }
auto end() const { return begin() + _n; }
auto begin() { return _data.get(); }
auto end() { return begin() + _n; }
auto size() const { return _n; }
private:
size_t _n {};
std::unique_ptr<T[]> _data;
};
That's 15 lines of code. See it Live
int main() {
using std::begin;
using std::end;
for (int n; (n = size_gen(prng)) != 10;) {
dyn_array<double> data(n);
std::iota(begin(data), end(data), 0);
static_assert(sizeof(data) == 2*sizeof(void*));
fmt::print("Size {} data {}\n", n, data);
}
}
Printing e.g.
Size 8 data {0, 1, 2, 3, 4, 5, 6, 7}
Size 6 data {0, 1, 2, 3, 4, 5}
Size 7 data {0, 1, 2, 3, 4, 5, 6}
Size 6 data {0, 1, 2, 3, 4, 5}
I have commented two versions that
add constructor guides with initializers live
and add copy/move semantics live
BONUS: Single Pointer Size
Building on the above, I realized that the size can be inside the allocation making the size of dyn_array exactly 1 pointer.
Live Demo
#include <memory>
#include <cstdlib> // committing the sin of malloc for optimization
template <typename T>
struct dyn_array {
dyn_array(std::initializer_list<T> init)
: _imp(allocate(init.size(), true))
{ std::uninitialized_move(init.begin(), init.end(), begin()); }
dyn_array(dyn_array const& rhs)
: _imp(allocate(rhs.size(), true))
{ std::uninitialized_copy(rhs.begin(), rhs.end(), begin()); }
dyn_array(dyn_array&& rhs) { rhs.swap(*this); }
dyn_array& operator=(dyn_array rhs) { rhs.swap(*this); }
explicit dyn_array(size_t n = 0)
: _imp(allocate(n))
{ }
auto size() const { return _imp? _imp->_n : 0ull; }
auto begin() const { return _imp? _imp->_data + 0 : nullptr; }
auto begin() { return _imp? _imp->_data + 0 : nullptr; }
auto end() const { return begin() + size(); }
auto end() { return begin() + size(); }
auto empty() const { return size() == 0; }
bool operator==(dyn_array const& rhs) const {
return size() == rhs.size() &&
std::equal(rhs.begin(), rhs.end(), begin());
};
void swap(dyn_array& rhs) {
std::swap(_imp, rhs._imp);
}
private:
struct Impl {
size_t _n;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
T _data[]; // C99 extension
#pragma GCC diagnostic pop
};
struct Deleter {
void operator()(Impl* s) const {
while (s->_n) { s->_data[--(s->_n)].~T(); }
std::free(s);
}
};
using Ptr = std::unique_ptr<Impl, Deleter>;
Ptr _imp;
static Ptr allocate(size_t n, bool uninitialized = false) {
if (!n)
return {};
auto p = std::malloc(sizeof(Impl) + n*sizeof(T)); // could be moreconservative
auto s = Ptr(reinterpret_cast<Impl*>(p));
s->_n = n;
if (!uninitialized)
std::uninitialized_default_construct_n(s->_data, n);
return s;
}
};
Which we can use as:
#include <fmt/ranges.h>
static size_t constructions = 0;
static size_t default_ctor, copy_ctor = 0;
static size_t destructions = 0;
struct Sensor final {
Sensor() { ++constructions; ++default_ctor; }
Sensor(Sensor const&) { ++constructions; ++copy_ctor; }
~Sensor() { ++destructions; }
};
int main() {
fmt::print("With initializers: {}, {}\n",
dyn_array{3.1415f},
dyn_array{"one", "two", "three"});
fmt::print("Without: {}, {}\n",
dyn_array<std::string_view>{3},
dyn_array<float>{1},
dyn_array<int>{}); // empty by default, no allocation
auto a = dyn_array{3,2,1};
fmt::print("sizeof(a) == sizeof(void*)? {}\n", sizeof(a) == sizeof(void*));
auto copy = a;
fmt::print("copy: {} == {}? {}\n", copy, a, (copy == a));
auto move = std::move(copy);
fmt::print("move: {} == {}? {}\n", move, a, (move == a));
fmt::print("copy now moved-from: {}, empty? {}\n", copy, copy.empty());
dyn_array<Sensor>(4); // test destructors
fmt::print("constructions({}) and destructions({}) match? {}\n",
constructions, destructions, constructions == destructions);
fmt::print("all default, no copy ctors? {}\n",
(copy_ctor == 0) && (default_ctor == constructions));
dyn_array { Sensor{}, Sensor{}, Sensor{} };
fmt::print("constructions({}) and destructions({}) match? {}\n",
constructions, destructions, constructions == destructions);
fmt::print("initializers({}) were uninitialized-copied: {}\n",
copy_ctor,
(copy_ctor == 3) && (default_ctor + copy_ctor == constructions));
}
Printing:
With initializers: {3.1415}, {"one", "two", "three"}
Without: {"", "", ""}, {1}
sizeof(a) == sizeof(void*)? true
copy: {3, 2, 1} == {3, 2, 1}? true
move: {3, 2, 1} == {3, 2, 1}? true
copy now moved-from: {}, empty? true
constructions(4) and destructions(4) match? true
all default, no copy ctors? true
constructions(10) and destructions(10) match? true
initializers(3) were uninitialized-copied: true
Of course you can do this without using C99 flexible array members, but I didn't want to meddle with alignment manually right now.
BONUS: [] Indexing, front, back, at accessors
These are really simple to add:
auto& operator[](size_t n) const { return *(begin() + n); }
auto& operator[](size_t n) { return *(begin() + n); }
auto& at(size_t n) const { return n<size()?*(begin() + n): throw std::out_of_range("dyn_array::at"); }
auto& at(size_t n) { return n<size()?*(begin() + n): throw std::out_of_range("dyn_array::at"); }
auto& front() const { return at(0); }
auto& front() { return at(0); }
auto& back() const { return at(size()-1); }
auto& back() { return at(size()-1); }
Of course push_back/erase/etc are out since the size doesn't change after construction.
I do not need to change its size after dynamic allocation (from the comments)
Since you do not need your vectors to be expanded dynamically, std::valarray<T> may be a good alternative. Its size is fixed at construction, so the implementation does not need a third pointer.
Related
std::vector<uint8_t> vector1 = { 1, 2, 3, 4 };
I would like to transform the vector above in its uint32_t version. I tried doing:
std::vector<uint32_t> vector2(vector1.begin(), vector2.end());
but this code returns the same array in 32 bit version, so the content is still { 1, 2, 3, 4 }.
What I expect to get is an array of a single value (in this case) of 0x01020304.
Is there any way to achieve that preferably without using for loop?
Thank you.
EDIT:
My solution, don't know if it's a good practice, but from what I learned about vectors it should be completely valid:
std::vector<uint32_t> vector2((uint32_t*)vector1.data(), (uint32_t*)(vector1.data() + vector1.size()*sizeof(uint8_t)));
You can use ranges, either by using the range-v3 library, or the C++20 std::ranges.
std::vector<uint8_t> vector1 = { 1, 2, 3, 4, 5, 6, 7, 8 };
std::vector<uint32_t> vec2 =
vector1
| view::chunk(4)
| views::transform(
[](auto&& range){
return accumulate(
range,
static_cast<uint32_t>(0),
[](auto acc, auto i)
{
return acc << 8 | i;
}
);
})
| to<std::vector>();
vec2 will contain {0x1020304, 0x5060708}.
See it on godbolt: https://godbolt.org/z/6z6ehfKbq
I recommend using a loop to do this for simplicity
#include <cstdint>
#include <exception>
#include <vector>
std::uint32_t
combine(std::uint8_t a,
std::uint8_t b,
std::uint8_t c,
std::uint8_t d)
{
return (std::uint32_t(a) << 24) |
(std::uint32_t(b) << 16) |
(std::uint32_t(c) << 8) |
std::uint32_t(d);
}
std::vector<std::uint32_t>
combine_vector(const std::vector<std::uint8_t>& items)
{
if (items.size() % 4 != 0)
throw std::exception();
std::vector<std::uint32_t> ret;
ret.reserve(items.size() / 4);
for (std::size_t i = 0; i < items.size(); i += 4) {
ret.push_back(
combine(items[i + 0],
items[i + 1],
items[i + 2],
items[i + 3]));
}
return ret;
}
I suspect you would need to implement a custom iterator type to do this without using a raw loop.
I made such an iterator
#include <iterator>
class combine_iterator
{
public:
using value_type = std::uint32_t;
using reference = const value_type&;
using pointer = const value_type*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
combine_iterator(const std::uint8_t* data, std::size_t count) :
m_data(data)
{
if (count % 4 != 0)
throw std::exception();
if (count > 0)
++*this;
}
reference
operator*() const
{
return m_cur_val;
}
pointer
operator->() const
{
return &m_cur_val;
}
friend combine_iterator&
operator++(combine_iterator& rhs)
{
std::uint8_t a = *(rhs.m_data++);
std::uint8_t b = *(rhs.m_data++);
std::uint8_t c = *(rhs.m_data++);
std::uint8_t d = *(rhs.m_data++);
rhs.m_cur_val = combine(a, b, c, d);
return rhs;
}
friend combine_iterator
operator++(combine_iterator& lhs, int)
{
auto cp = lhs;
++lhs;
return cp;
}
friend bool
operator!=(const combine_iterator& lhs, const combine_iterator& rhs)
{
return (lhs.m_data != rhs.m_data);
}
private:
const std::uint8_t* m_data;
std::uint32_t m_cur_val;
};
int main()
{
std::vector<std::uint8_t> data = { 1, 2, 3, 4, 5, 6, 7, 8 };
std::vector<std::uint32_t> res(
combine_iterator(data.data(), data.size()),
combine_iterator(data.data() + data.size(), 0));
}
The iterator contains at least one bug. I'm leaving the bugs in as an educational lesson why using a loop if often easier to get correct than implementing custom iterators. Custom iterators should ideally only be created if we create a container which needs it, or the mental overhead of creating a custom iterator can be justified.
I'm just going to come out and ask. I have 500 arrays[127]. I need to sort them all by the value of the very last element in descending order. How would you do this in the most efficient way possible?
EXAMPLE:
float arr1[] = {3,1,4,5};
float arr2[] = {1,2,5,8};
float arr3[] = {102,4,132,2};
OUTPUT:
{arr2,
arr1,
arr3}
Your could create a lightweight wrapper that just stores the begin and end iterators of an array.
#include <iterator>
template<class T>
struct span {
template<size_t N>
span(T (&arr)[N]) : m_begin(std::begin(arr)), m_end(std::end(arr)) {}
T* begin() const { return m_begin; }
T* end() const { return m_end; }
T& front() const { return *m_begin; }
T& back() const { return *std::prev(m_end); }
private:
T* m_begin;
T* m_end;
};
You could then put these lightweight wrappers in a std::vector<span<int>> and sort that. Note that this will not affect the original arrays in any way and since sorting the vector only moves the spans around and not the actual array data, it should be reasonably fast.
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
int arr1[] = {3,1,4,5};
int arr2[] = {1,2,5,8};
int arr3[] = {102,4,132,2};
std::vector<span<int>> arrs{
arr1, arr2, arr3
};
// sort on the last element, decending:
std::sort(arrs.begin(), arrs.end(),[](const auto& lhs, const auto& rhs) {
return rhs.back() < lhs.back();
});
for(auto& arr : arrs) {
for(auto v : arr) std::cout << v << ' ';
std::cout << '\n';
}
}
Output:
1 2 5 8
3 1 4 5
102 4 132 2
Since a raw array is a pointer without size information carried out, your job isn't automatic.
Either you convert all your arrays to std::vector<float> so you can have the last element and then have a std::vector<std::vector<float>> with a custom sort operator to test, or you create a structure that contains the raw array along with size information.
std::vector<float> arr1 = {3,1,4,5}
std::vector<float> arr2 = {1,2,5,8};
std::vector<float> arr3 = {102,4,132,2};
std::vector<std::vector<float>> arrays = {arr1,arr2,arr3};
std::sort(arrays.begin(),arrays.end(),[](const std::vector<float>& r1,const std::vector<float>& r2) -> bool
{
if (r1[r1.size() - 1] < r2[r2.size() - 1]) return true;
return false;
});
struct ArrAndSize { float* arr; size_t sz; };
ArrAndSize arr1 = {{3,1,4,5},4};
ArrAndSize arr2 = {{1,2,5,8},4};
ArrAndSize arr3 = {{102,4,132,2},4};
std::vector<ArrAndSize> arrays = {arr1,arr2,arr3};
std::sort(arrays.begin(),arrays.end(),[](const ArrAndSize& r1,const ArrAndSize& r2) -> bool
{
if (r1.arr[r1.sz - 1] < r2.arr[r2.sz - 1]) return true;
return false;
});
In both cases, check if the array has size 0.
I've been thinking if it is possible to actually let the user decide how many "dimensions" an array should have, based on a number given.
Let n be the number of dimensions, the user will type in its value.
It will create an array with n dimensions.
Example: for n=5, it will create an array called list, like that: int list[size1][size2][size3][size4][size5].
size variables will still be mentioned by the user, but that's actually part 2.
I want to know if I can add more dimensions to an array, after I have declared it. And if not, I want to find a solution to this problem.
The C++ language does not have provision for variable-sized or variable-dimensioned arrays.
You can, however, create a class to encapsulate these behaviors.
The important characteristic is the dimensions. You can use a std::vector<int> to track the number of elements per dimension; for example, {3, 4, 5} to represent a three-dimensional matrix where the rank of the innermost dimension is 3, the middle 4, and the outer 5.
Use a templated vector or deque to allocate space for the elements. The number of elements required is the product of the dimension ranks. (You can use std::accumulate with a multiplication operator to compute this over your ranks vector.)
Next, you'll need a method that takes some object (say, a vector of int) that provides all the indices into the MD-array necessary to access an element. You can provide overloads that take a variable number of arguments using some fancy template metaprogramming.
All of this is overkill outside of some very specialized uses, such as: you are writing Mathematica-like software that allows users to play with these things.
You may be interested in an array class I implemented a few months ago that aims to provide a syntax for arrays that mimics that of matlab arrays. It utilizes initilizer_list syntax to allow for arbitrary dimensional arrays to be created using
Array<double> array({10, 20, 30});
You can then access and modify individual elements using
double d = array[{1, 2, 3}];
array[{1, 2, 3}] = 10;
And even slice the matrix up into pieces using
array.getSlice({___, 3, 4});
where "___" is used as a wildcard.
See more on: http://www.second-quantization.com/Array.html
Implementation: https://github.com/dafer45/TBTK/blob/master/Lib/include/Utilities/TBTK/Array.h
Solution for object let's the user choose the number of dimensions. A little robust, my C++ maybe is not the best, but it was fun implementing. nvector<T> represents resizable (in dimensions and count of elements in each dimension) array of element of T type, although only some resize functions are implemented. narray<T> is the same, but the number of dimensions is not resizable. This works around the idea of recalculating index position of a multidimensional array using a single continuous array.
#include <cstdio>
#include <vector>
#include <iostream>
#include <cstddef>
#include <cstdarg>
#include <algorithm>
#include <numeric>
#include <cassert>
#include <memory>
#include <cstring>
using namespace std;
template<typename T>
class narray {
public:
static size_t compute_size(initializer_list<size_t>& dims) {
return accumulate(dims.begin(), dims.end(), 1, multiplies<size_t>());
}
static size_t compute_size(vector<size_t>& dims) {
return accumulate(dims.begin(), dims.end(), 1, multiplies<size_t>());
}
static size_t compute_distance(vector<size_t>& dims) {
return dims.size() > 1 ? dims[1] : 1;
}
static vector<size_t> remove_one_dim(vector<size_t> dims_) {
return vector<size_t>(dims_.begin() + 1, dims_.end());
}
narray(initializer_list<size_t> dims, T* data) :
dims_(dims), data_(data) {}
narray(vector<size_t> dims, T* data) :
dims_(dims), data_(data) {}
T operator*() {
return *data_;
}
T* operator&() {
return data_;
}
void operator=(T v) {
if (dims_.size() != 0)
throw runtime_error(__PRETTY_FUNCTION__);
*data_ = v;
}
void operator=(initializer_list<T> v) {
if (v.size() > size())
throw runtime_error(__PRETTY_FUNCTION__);
copy(v.begin(), v.end(), data_);
}
T* data() {
return data_;
}
T* data_last() {
return &data()[compute_size(dims_)];
}
size_t size() {
return compute_size(dims_);
}
size_t size(size_t idx) {
return dims_[idx];
}
narray<T> operator[](size_t idx) {
if (dims_.size() == 0)
throw runtime_error(__PRETTY_FUNCTION__);
return narray<T>(remove_one_dim(dims_),
&data_[idx * compute_distance(dims_)]);
}
class iterator {
public:
iterator(initializer_list<size_t>& dims, T* data) :
dims_(dims), data_(data) { }
iterator(vector<size_t>& dims, T* data) :
dims_(dims), data_(data) { }
iterator operator++() {
iterator i = *this;
data_ += compute_distance(dims_);
return i;
}
narray<T> operator*() {
return narray<T>(remove_one_dim(dims_), data_);
}
bool operator!=(const iterator& rhs) {
if (dims_ != rhs.dims_)
throw runtime_error(__PRETTY_FUNCTION__);
return data_ != rhs.data_;
}
private:
vector<size_t> dims_;
T* data_;
};
iterator begin() {
return iterator(dims_, data());
}
iterator end() {
return iterator(dims_, data_last());
}
private:
vector<size_t> dims_;
T* data_;
};
template<typename T>
class nvector {
public:
nvector(initializer_list<size_t> dims) :
dims_(dims), data_(narray<T>::compute_size(dims)) {}
nvector(vector<size_t> dims) :
dims_(dims), data_(narray<T>::compute_size(dims)) {}
nvector(initializer_list<size_t> dims, T* data) :
dims_(dims), data_(data) {}
nvector(vector<size_t> dims, T* data) :
dims_(dims), data_(data) {}
T* data() {
return data_.data();
}
T* data_last() {
return &data()[narray<T>::compute_size(dims_)];
}
size_t size() {
return narray<T>::compute_size(dims_);
}
narray<T> operator&() {
return narray<T>(dims_, data());
}
narray<T> operator[](size_t idx) {
if (dims_.size() == 0)
throw runtime_error(__PRETTY_FUNCTION__);
return narray<T>(narray<T>::remove_one_dim(dims_),
&data()[idx * narray<T>::compute_distance(dims_)]);
}
void operator=(initializer_list<T> v) {
if (v.size() > size())
throw runtime_error(__PRETTY_FUNCTION__);
copy(v.begin(), v.end(), data_.begin());
}
auto begin() {
return typename narray<T>::iterator(dims_, data());
}
auto end() {
return typename narray<T>::iterator(dims_, data_last());
}
// add and remove dimensions
void dimension_push_back(size_t dimsize) {
dims_.push_back(dimsize);
data_.resize(size());
}
void dimension_pop_back() {
dims_.pop_back();
data_.resize(size());
}
// TODO: resize dimension of index idx?
private:
vector<size_t> dims_;
vector<T> data_;
};
int main()
{
nvector<int> A({2, 3});
A = { 1,2,3, 4,5,6 };
assert(A.size() == 6);
assert(&A[0] == &A.data()[0]);
assert(&A[0][0] == &A.data()[0]);
assert(&A[1] == &A.data()[3]);
assert(&A[0][1] == &A.data()[1]);
assert(&A[1][1] == &A.data()[4]);
cout << "Currently array has " << A.size() << " elements: " << endl;
for(narray<int> arr1 : A) { // we iterate over arrays/dimensions
for(narray<int> arr2 : arr1) { // the last array has no dimensions
cout << "elem: " << *arr2 << endl;
}
}
cout << endl;
// assigment example
cout << "Now it is 4: " << *A[1][0] << endl;
A[1][0] = 10;
cout << "Now it is 10: " << *A[1][0] << endl;
return 0;
}
This code needs still much more work. It works only as a simple example. Maybe use shared_ptr in narray? Implement better exceptions?
So creating an array of n=5 dimensions with sizes size1, size2, size3, size4 and size5 would like this:
narray<int> arr({size1, size2, size3, size4, size5});
arr[0][1][2][3][4] = 5; // yay!
Is there a way to use for-range loop syntax to process two sequential elements in an array?
Example...
func( std::vector< vec2 > &points )
{
std::vector< float > distances;
for( int i = 0; i < (int)points.size() - 1; i++ )
{
auto from = points.at( i );
auto to = points.at( i + 1 );
distances.push_back( magnitude( to - from ) );
}
}
Is there a way to use for-range loop syntax to process two sequential elements in an array?
Not out of the box.
However, you can roll your own wrapper class and an iterator class to get what you need.
The begin() and end() member functions of the wrapper class must return an iterator which evaluates to a std::pair when it is dereferenced with the * operator.
Here's a demonstrative program:
#include <iostream>
#include <vector>
struct VectorWrapper;
struct MyIterator
{
MyIterator(VectorWrapper const& wrapper, size_t index) : wrapper_(wrapper), index_(index) {}
std::pair<float, float> operator*();
MyIterator& operator++()
{
++index_;
return *this;
}
bool operator==(MyIterator const& rhs) const
{
return (this->index_ == rhs.index_);
}
bool operator!=(MyIterator const& rhs) const
{
return (this->index_ != rhs.index_);
}
VectorWrapper const& wrapper_;
size_t index_;
};
struct VectorWrapper
{
explicit VectorWrapper(std::vector<float>& distances) : distances_(distances) {}
MyIterator begin() const
{
return MyIterator(*this, 0);
}
MyIterator end() const
{
return MyIterator(*this, distances_.size()-1);
}
std::vector<float>& distances_;
};
std::pair<float, float> MyIterator::operator*()
{
return std::make_pair(wrapper_.distances_[index_], wrapper_.distances_[index_+1]);
}
int main()
{
std::vector<float> dist = {1, 2, 3, 4, 5, 6};
VectorWrapper wrapper(dist);
for ( auto item : wrapper )
{
std::cout << item.first << ", " << item.second << std::endl;
}
}
and its output:
1, 2
2, 3
3, 4
4, 5
5, 6
In c++17 with a bit of library help, you can get this to work:
for (auto&&[ from, to ] : adjacent_overlapped_zip( points ) ) {
distances.push_back( magnitude( to-from) );
}
where adjacent_overlapped_zip returns a range of adpted iterators over pairs or tuples of points.
template<class It>
struct range {
It b; It e;
It begin() const{ return b; }
It end() const{ return e; }
bool empty() const{ return begin()==end(); }
range without_front( std::size_t n = 1 ) const {
return {std::next(begin(), n), end()};
}
range without_back( std::size_t n = 1 ) const {
return {begin(), std::prev(end(), n)};
}
};
template<class It>
range(It b, It e)->range<It>;
template<class It>
struct adjacent_iterator:It {
auto operator*()const {
return std::make_pair( It::operator*(), std::next(*this).It::operator*() );
}
using It::It;
explicit adjacent_iterator(It it):It(it) {}
};
template<class It>
explicit adjacent_iterator( It ) -> adjacent_iterator<It>;
// TODO: support pointers
template<class C>
auto adjacent_overlapped_zip( C& c ) {
using std::begin; using std::end;
range r( begin(c), end(c) );
if (!r.empty()) {
r = r.without_back();
range retval( adjacent_iterator(r.begin()), adjacent_iterator(r.end()) );
return retval;
} else {
return {};
}
}
or something like that. The above code probably contains typos and other errors.
I'd also be tempted by:
for (auto&&[ from, to ] : transform( [](auto it){ return std::make_pair( *it, *std::next(it)); }, range( iterators_of( points ) ).without_back() ) )
distances.push_back( magnitude( to-from) );
}
with a slightly fancier set of primitives. Ranges-v3 would make this even nicer.
In raw c++11, no you are out of luck.
Can I get the positions of the max N elements (the equal max elements) using predefined function in STL?
A solution I thought of is:
vector<int> maxN(vector<int> original){
vector<int> result;
auto pt = max_element(original.begin(),original.end());
int max = *pt;
while(*pt == max){
result.push_back(distance(original.begin(),pt));
*pt = 0;//assumed that all the elements in original are greater than 0
pt = max_element(original.begin(),original.end());
}
return result;
}
There must be a more elegant way to do this.
It depends on your exact requirements:
std::max_element gives you the maximum element. std::copy_if can be used to copy all elements equal to the maximum (and limit the maximum number if required, e.g. using a lambda).
std::nth_element partially sorts a range (e.g. your vector) such the first n entries are equal or less to anything that follows. The first n elements are not sorted themselves. And it is not a stable partition.
std::partial_sort gives you the same, but the first n elements are sorted. Again, not a stable partition/sort.
Combine std::nth_element + std::stable_partition + std::stable_sort if you need a stable selection of the first n elements and you want them stably sorted.
Once you have found the max element, make another linear pass over the vector to find all the matching elements. Setting to 0 is not needed when doing it this way. The original vector is being passed by value, so setting to 0 was not being seen by the caller. This makes for a very clear implementation:
vector<int> maxN(vector<int> original){
vector<int> result;
if (original.empty()) return result;
const int max = *(max_element(original.begin(), original.end()));
for (int i = 0; i < original.size(); ++i) {
if (original[i] == max) result.push_back(i);
}
return result;
}
It is more importatnt to implement clear and maintainable code than to attempt to extract maximal reuse from the C++ library.
If your goal is to not use an explicit loop over the passed in original vector, but use some standard C++ template algorithm, I recommend creating a helper iterator to help you recover the index.
struct indexer {
int i_;
indexer (int i = 0) : i_(i) {}
indexer & operator ++ () { ++i_; return *this; }
indexer operator ++ (int) { ++i_; return i_ - 1; }
int operator * () const { return i_; }
bool operator != (indexer rhs) const { return i_ != rhs.i_; }
//... whatever else is required for copy_if
};
Then, you can invoke copy_if with a simple lambda and a back insert iterator:
copy_if(indexer(), indexer(original.size()), back_inserter(result),
[&](int i) -> bool { return original[i] == max; });
However, this is more obscure than the straightforward loop presented above.
As variant (can check on a cpp.sh)
#include <iostream>
#include <vector>
int main ()
{
std::vector<int>elems = {10, 20, 10, 30, 5, 30, 8, 30, 18, 12};
for(size_t i=0; i<elems.size()-1; i++)
{
if(elems[i+1] > elems[i]) { elems.erase(elems.begin()+i); i=-1;}
else if(elems[i+1] < elems[i]) { elems.erase(elems.begin()+i+1); i=-1;}
}
return 0;
}
You could decorate your original with indices, take the Nth element-approach, and strip off the indices again (test on cpp.sh):
template<typename T, typename less = std::greater<T>>
std::vector<int> max_indices(
int N,
const std::vector<T> &original,
less predicate = less())
{
auto decorated = with_indices(original);
// the gist of the problem
const auto nth = std::next(begin(decorated), N);
std::nth_element(begin(decorated), nth, end(decorated),
with_indices(predicate));
std::sort(begin(decorated), nth,
with_indices(predicate));
decorated.erase(nth, end(decorated));
return indices(decorated);
}
int main()
{
std::vector<int> values{ {1, 2, 3 , 4, 5, 6, 7, 8, 9, 10} };
auto m = max_indices(4, values);
assert(4u == m.size());
assert(9 == m[0]);
assert(8 == m[1]);
assert(7 == m[2]);
assert(6 == m[3]);
return 0;
}
Where these functions do the decorating/undecorating:
template<typename T>
std::vector<std::pair<T, int>> with_indices(const std::vector<T> &original)
{
std::vector< std::pair<T, int> > decorated;
std::transform(begin(original), end(original), std::back_inserter(decorated),
[index = 0](T t) mutable {
return std::make_pair(t, index++);
});
return decorated;
}
template<typename T>
std::vector<int> indices(const std::vector<std::pair<T, int>> &original)
{
std::vector<int> undecorated;
std::transform(begin(original), end(original), std::back_inserter(undecorated),
[](auto p) mutable {
return p.second;
});
return undecorated;
}
template<typename Function>
auto with_indices(Function f)
{
return [&](auto... args) {
return f(args.first...);
};
}