使用boost :: join访问和更改不同数组的值是否更好?
我在类element
中定义了一个成员数组。
class element
{
public:
element();
int* get_arr();
private:
int m_arr[4];
}
在不同的地方,我正在访问这些数组,并使用boost :: join和更改数组值连接在一起。
//std::vector<element> elem;
auto temp1 = boost::join(elem[0].get_arr(),elem[1].get_arr());
auto joined_arr = boost::join(temp1,elem[2].get_arr());
//now going to change the values of the sub array
for(auto& it:joined_arr)
{
it+= sample[i];
i++;
}
如上所述修改类中数组的值是个好主意吗?
答案 0 :(得分:3)
在您的代码中,您可能希望加入4元素数组。为此,请将get_arr
的签名更改为:
typedef int array[4];
array& get_arr() { return m_arr; }
这样数组大小就不会丢失。
在性能方面,通过联接视图访问元素的成本非零。双循环将是最有效的,并且也易于阅读,例如:
for(auto& e : elem)
for(auto& a : e.get_arr())
a += sample[i++];
答案 1 :(得分:2)
boost::join
每个合成步骤都会返回一个更复杂的类型。在某些时候,您可能会超出编译器对内联的限制,因此您将获得运行时成本¹。
在盒子外面思考,看起来你真的想要创建一个缓冲区抽象,它允许你像IO一样进行分散/收集,只需很少的分配。
碰巧,Boost Asio对此有很好的抽象²,你可以使用它:http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/MutableBufferSequence.html
正如我在earlier iteration of this answer code中发现的那样,抽象可悲地仅适用于通过本地char类型元素访问的缓冲区。那不好。
因此,在这次重写中,我提出了一个类似的抽象,它只包含一个“层次迭代器”,它知道如何迭代一系列“缓冲区”(在这个实现中,任何范围都可以)。
您可以选择直接操作一系列范围,例如:
std::vector<element> seq(3); // tie 3 elements together as buffer sequence
element& b = seq[1];
或者,没有任何进一步的改变,参考:
element a, b, c;
std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence
底部的C ++版本演示了这种方法 Live On Coliru
我使用过Boost Range和Boost Iterator:
template <typename Seq,
typename WR = typename Seq::value_type,
typename R = typename detail::unwrap<WR>::type,
typename V = typename boost::range_value<R>::type
>
struct sequence_iterator : boost::iterator_facade<sequence_iterator<Seq,WR,R,V>, V, boost::forward_traversal_tag> {
using OuterIt = typename boost::range_iterator<Seq>::type;
using InnerIt = typename boost::range_iterator<R>::type;
// state
Seq& _seq;
OuterIt _ocur, _oend;
InnerIt _icur, _iend;
static sequence_iterator begin(Seq& seq) { return {seq, boost::begin(seq), boost::end(seq)}; }
static sequence_iterator end(Seq& seq) { return {seq, boost::end(seq), boost::end(seq)}; }
// the 3 facade operations
bool equal(sequence_iterator const& rhs) const {
return ((_ocur==_oend) && (rhs._ocur==rhs._oend))
|| (std::addressof(_seq) == std::addressof(rhs._seq) &&
_ocur == rhs._ocur && _oend == rhs._oend &&
_icur == rhs._icur && _iend == rhs._iend);
}
void increment() {
if (++_icur == _iend) {
++_ocur;
setup();
}
}
V& dereference() const {
assert(_ocur != _oend);
assert(_icur != _iend);
return *_icur;
}
private:
void setup() { // to be called after entering a new sub-range in the sequence
while (_ocur != _oend) {
_icur = boost::begin(detail::get(*_ocur));
_iend = boost::end(detail::get(*_ocur));
if (_icur != _iend)
break;
++_ocur; // skid over, this enables simple increment() logic
}
}
sequence_iterator(Seq& seq, OuterIt cur, OuterIt end)
: _seq(seq), _ocur(cur), _oend(end) { setup(); }
};
这与boost::asio::buffers_iterator
基本上是同一种迭代器,但它不假设元素类型。现在,为任何范围序列创建sequence_iterator
都非常简单:
template <typename Seq> auto buffers_begin(Seq& seq) { return sequence_iterator<Seq>::begin(seq); }
template <typename Seq> auto buffers_end(Seq& seq) { return sequence_iterator<Seq>::end(seq); }
<强> Live On Coliru 强>
// DEMO
struct element {
int peek_first() const { return m_arr[0]; }
auto begin() const { return std::begin(m_arr); }
auto end() const { return std::end(m_arr); }
auto begin() { return std::begin(m_arr); }
auto end() { return std::end(m_arr); }
private:
int m_arr[4] { };
};
namespace boost { // range adapt
template <> struct range_iterator<element> { using type = int*; };
// not used, but for completeness:
template <> struct range_iterator<element const> { using type = int const*; };
template <> struct range_const_iterator<element> : range_iterator<element const> {};
}
#include <algorithm>
#include <iostream>
#include <vector>
template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
auto ib = boost::begin(input), ie = boost::end(input);
auto ob = boost::begin(output), oe = boost::end(output);
size_t n = 0;
for (;ib!=ie && ob!=oe; ++n) {
op(*ob++, *ib++);
}
return n;
}
int main() {
element a, b, c;
std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence
//// Also supported, container of range objects directly:
// std::list<element> seq(3); // tie 3 elements together as buffer sequence
// element& b = seq[1];
std::vector<int> const samples {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32
};
using boost::make_iterator_range;
auto input = make_iterator_range(samples);
auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));
while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
input.advance_begin(n);
}
}
打印
Copied 12 samples, b starts with 5
Copied 12 samples, b starts with 22
Copied 8 samples, b starts with 51
<强> Live On Coliru 强>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <functional> // std::reference_wrapper
namespace detail {
template<typename T> constexpr T& get(T &t) { return t; }
template<typename T> constexpr T const& get(T const &t) { return t; }
template<typename T> constexpr T& get(std::reference_wrapper<T> rt) { return rt; }
template <typename T> struct unwrap { using type = T; };
template <typename T> struct unwrap<std::reference_wrapper<T> > { using type = T; };
}
template <typename Seq,
typename WR = typename Seq::value_type,
typename R = typename detail::unwrap<WR>::type,
typename V = typename boost::range_value<R>::type
>
struct sequence_iterator : boost::iterator_facade<sequence_iterator<Seq,WR,R,V>, V, boost::forward_traversal_tag> {
using OuterIt = typename boost::range_iterator<Seq>::type;
using InnerIt = typename boost::range_iterator<R>::type;
// state
Seq& _seq;
OuterIt _ocur, _oend;
InnerIt _icur, _iend;
static sequence_iterator begin(Seq& seq) { return {seq, boost::begin(seq), boost::end(seq)}; }
static sequence_iterator end(Seq& seq) { return {seq, boost::end(seq), boost::end(seq)}; }
// the 3 facade operations
bool equal(sequence_iterator const& rhs) const {
return ((_ocur==_oend) && (rhs._ocur==rhs._oend))
|| (std::addressof(_seq) == std::addressof(rhs._seq) &&
_ocur == rhs._ocur && _oend == rhs._oend &&
_icur == rhs._icur && _iend == rhs._iend);
}
void increment() {
if (++_icur == _iend) {
++_ocur;
setup();
}
}
V& dereference() const {
assert(_ocur != _oend);
assert(_icur != _iend);
return *_icur;
}
private:
void setup() { // to be called after entering a new sub-range in the sequence
while (_ocur != _oend) {
_icur = boost::begin(detail::get(*_ocur));
_iend = boost::end(detail::get(*_ocur));
if (_icur != _iend)
break;
++_ocur; // skid over, this enables simple increment() logic
}
}
sequence_iterator(Seq& seq, OuterIt cur, OuterIt end)
: _seq(seq), _ocur(cur), _oend(end) { setup(); }
};
template <typename Seq> auto buffers_begin(Seq& seq) { return sequence_iterator<Seq>::begin(seq); }
template <typename Seq> auto buffers_end(Seq& seq) { return sequence_iterator<Seq>::end(seq); }
// DEMO
struct element {
int peek_first() const { return m_arr[0]; }
auto begin() const { return std::begin(m_arr); }
auto end() const { return std::end(m_arr); }
auto begin() { return std::begin(m_arr); }
auto end() { return std::end(m_arr); }
private:
int m_arr[4] { };
};
namespace boost { // range adapt
template <> struct range_iterator<element> { using type = int*; };
// not used, but for completeness:
template <> struct range_iterator<element const> { using type = int const*; };
template <> struct range_const_iterator<element> : range_iterator<element const> {};
}
#include <algorithm>
#include <iostream>
#include <vector>
template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
auto ib = boost::begin(input), ie = boost::end(input);
auto ob = boost::begin(output), oe = boost::end(output);
size_t n = 0;
for (;ib!=ie && ob!=oe; ++n) {
op(*ob++, *ib++);
}
return n;
}
int main() {
element a, b, c;
std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence
//// Also supported, container of range objects directly:
// std::list<element> seq(3); // tie 3 elements together as buffer sequence
// element& b = seq[1];
std::vector<int> const samples {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32
};
using boost::make_iterator_range;
auto input = make_iterator_range(samples);
auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));
while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
input.advance_begin(n);
}
}
¹我忽略了编译时成本和auto x = complicated_range_composition
模式的潜伏危险,当复杂范围组合包含对临时工具的引用时:这是UB错误的常见来源
²已被各种其他库采用,如Boost Beast,Boost Process,似乎已经进入了C ++ 20的网络TS:Header <experimental/buffer>
synopsis (PDF)