boost::adapter::为没有constbegin/end的类转换



我正试图将一个对象传递给boost::adaptors::transformed。然而,只有当该对象的类定义了beginendconst版本时,这似乎才有效。然而,我的情况并非如此,因为对此类对象进行迭代会修改对象本身的内部状态。这里有一个使用伪类Vector的最小示例,该类只公开非常量版本beginend或其_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
}

一般来说,迭代修改集合是一种代码气味。

当然,某些东西在逻辑上可以是常量,这就是我们使用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}

更纯粹的方法:可变元素数据

为了好玩,让我们制作一个元素来跟踪其值被观察的次数:

实时编译器资源管理器

#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})

相关内容

  • 没有找到相关文章

最新更新