0

我正在尝试将对象传递给boost::adaptors::transformed. 但是,这似乎只有在该对象的类定义了和的const版本时才有效。然而,这对我来说不是这样,因为迭代这个类的对象会修改对象本身的内部状态。这是一个使用仅公开非 const 版本和/或它的成员的虚拟类的最小示例,我可以更改什么来使其工作?beginendVectorbeginend_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
}
4

1 回答 1

2

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

当然,某些东西在逻辑上也可以是const,这就是我们的mutable关键字。我可以看到大约有两种方法

请记住,线程感知库可能会假设const操作是线程安全保证(因此要么按位不可变,要么例如仅对成员互斥锁等同步原语进行操作)。

使容器存储可变

Live On 编译器资源管理器

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

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

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

Live On 编译器资源管理器

#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})
于 2020-11-18T16:01:31.680 回答