提问人:Peter 提问时间:11/18/2020 最后编辑:Peter 更新时间:11/19/2020 访问量:142
boost::adaptors::transformed,用于不带 const begin/end 的类
boost::adaptors::transformed for classes without const begin/end
问:
我正在尝试将一个对象传递给 .但是,这似乎只有在该对象的类定义了 和 的版本时才有效。然而,对我来说并非如此,因为遍历此类的对象会修改对象本身的内部状态。这是一个使用虚拟类的最小示例,该虚拟类仅公开非 const 版本和/或其成员,我可以更改什么来使其工作?boost::adaptors::transformed
const
begin
end
Vector
begin
end
_v
#include <initializer_list>
#include <vector>
#include <boost/range/adaptors.hpp>
template<typename T>
class Vector
{
public:
using value_type = typename std::vector<T>::value_type;
using reference = typename std::vector<T>::reference;
using iterator = typename std::vector<T>::iterator;
Vector(std::initializer_list<T> init)
: _v(init)
{}
iterator begin() { return _v.begin(); }
iterator end() { return _v.end(); }
private:
std::vector<T> _v;
};
int main()
{
Vector<int> v{1, 2, 3, 4};
auto t = [](int i){ return 2 * i; };
auto range(v | boost::adaptors::transformed(t)); // does not compile
}
答:
2赞
sehe
11/19/2020
#1
我想说的是,一般来说,迭代修改集合是一种代码气味。
当然,某些东西在逻辑上是常量的,这就是我们的关键词。我可以看到大约两种方法mutable
请记住,线程感知库可能会假设操作是线程安全保证(因此,要么是按位不可变的,要么仅在同步基元(如成员互斥锁)上操作)。
const
使容器存储可变
#include <initializer_list>
#include <vector>
#include <boost/range/adaptors.hpp>
#include <fmt/ranges.h>
using boost::adaptors::transformed;
template<typename T>
class Vector {
using Cont = std::vector<T>;
public:
using value_type = typename Cont::value_type;
using reference = typename Cont::reference;
using iterator = typename Cont::iterator;
using const_iterator = typename Cont::const_iterator;
Vector(std::initializer_list<T> init) : _v(init) {}
iterator begin() { return _v.begin(); }
iterator end() { return _v.end(); }
const_iterator begin() const { return _v.begin(); }
const_iterator end() const { return _v.end(); }
//const_iterator cbegin() const { return _v.begin(); }
//const_iterator cend() const { return _v.end(); }
private:
Cont mutable _v;
};
static auto twice(int i) { return 2 * i; }
int main() {
fmt::print("{} -> {}\n",
Vector {1, 2, 3, 4},
Vector {1, 2, 3, 4} | transformed(twice));
}
指纹
{1, 2, 3, 4} -> {2, 4, 6, 8}
更纯粹的方法:可变元素数据
为了好玩,让我们创建一个 Element 来跟踪其值被观察到的次数:
#include <initializer_list>
#include <vector>
#include <boost/range/adaptors.hpp>
#include <fmt/ranges.h>
using boost::adaptors::transformed;
struct Element {
Element(int value) : value(value) {}
operator int() const { ++usage_counter; return value; }
long usages() const { return usage_counter; }
private:
mutable long usage_counter = 0;
int value;
};
template<typename T>
class Vector {
using Cont = std::vector<T>;
public:
using value_type = typename Cont::value_type;
using reference = typename Cont::reference;
using iterator = typename Cont::iterator;
using const_iterator = typename Cont::const_iterator;
Vector(std::initializer_list<T> init) : _v(init) {}
iterator begin() { return _v.begin(); }
iterator end() { return _v.end(); }
const_iterator begin() const { return _v.begin(); }
const_iterator end() const { return _v.end(); }
//const_iterator cbegin() const { return _v.begin(); }
//const_iterator cend() const { return _v.end(); }
private:
Cont _v;
};
static auto twice(int i) { return 2 * i; }
int main() {
Vector<Element> const v {1, 2, 3, 4}; // note const
fmt::print("{} -> {} (usages {})\n",
v,
v | transformed(twice),
v | transformed(std::mem_fn(&Element::usages))
);
}
指纹
{1, 2, 3, 4} -> {2, 4, 6, 8} (usages {3, 3, 3, 3})
评论
1赞
Peter
11/20/2020
呵呵,是的,我想 mutable 解决了这个问题。我同意问题本身很奇怪,我没有创作有问题的集合,也许将其相关状态移动到每个迭代器都拥有shared_ptr的单独对象中会更有意义。
评论
mutable std::vector<T> _v;
#include <array> #include <iostream> int main(int argc,const char **argv) { std::array<int,4> src = {1, 2, 3, 4}; std::array<int,4> dst; for(std::size_t i=0; i < src.size(); i++) { dst[i] = src[i] << 1; } return 0; }