【问题标题】:How to provide multiple begin/end proxies for a class如何为一个类提供多个开始/结束代理
【发布时间】:2019-11-30 20:00:24
【问题描述】:

给定类

struct Data
{
  void bar() const;
  void baz();
}

class Foo
{
  std::vector<Data> data;
  std::map<size_t, Data> indexed_data;
}

我想在 Foo 类中实现一些东西,以便我可以执行以下操作:

int main()
{
  Foo foo;

  for(const auto& data : foo.data())
    data.bar();

  for(auto& data : foo.indexed_data())
    data.baz();

  const auto& foo_ref = foo;
  for(auto& data : foo_ref.data())
    data.baz();  // constness violated, shouldn't compile
}

但是,我不想仅仅通过返回对容器的引用来公开类内部。我也可以使用我想要迭代的范围没有作为容器实现的类。所以我基本上想创建某种代理对象,它比开始/结束对的包装器多一点,这样我就可以在我的类中迭代多个东西。 此外,我希望它是正确的,如上所示。是否有任何众所周知的模式来实现这一点?

【问题讨论】:

标签: c++ iterator range c++14 const-correctness


【解决方案1】:

考虑三种情况。


如果您想完全访问您的内部数据,只需创建一个函数来返回它:(只需将成员设为公开也是一种选择)

class C {
public:
          Type& data()       { return data_; }
    const Type& data() const { return data_; }
private:
    Type data_;
};

如果您想授予对内部数据的只读访问权限,只需删除非常量重载:

class C {
public:
    const Type& data() const { return data_; }
private:
    Type data_;
};

如果您想授予对内部数据的仅元素访问权限,即,您可以对每个单独的元素进行可变访问(当C 本身是非常量时),但您不能更改容器本身(例如,插入一个新元素),您需要返回一个代理。从 C++20 开始,我们可以返回一个std::ranges::ref_view

class C {
public:
    auto data()       { return std::ranges::ref_view(data_); }
    auto data() const { return std::ranges::ref_view(data_); }
private:
    Type data_;
};

如果 C++20 不可用,您可以使用 Ranges library。这样,用户可以访问各个元素,但不能更改容器本身。

或者,您可以编写自己的(极简)代理:

template <typename R>
class Proxy {
public:
    explicit Proxy(R& r) :range{r} {}
    auto begin() const { return range.begin(); }
    auto   end() const { return range.end(); }
private:
    R& range;
};

那你可以回Proxy{data_}:

class C {
public:
    auto data()       { return Proxy{data_}; }
    auto data() const { return Proxy{data_}; }
private:
    Type data_;
};

在C++17之前,不用类模板参数推导可以这样写:

class C {
public:
    auto data()       { return Proxy<      Type>{data_}; }
    auto data() const { return Proxy<const Type>{data_}; }
private:
    Type data_;
};

【讨论】:

  • 代理几乎是我一直想出的,但到目前为止我无法让它工作。因为要通过 const 引用或指向 C 的指针检索代理,我必须使 auto data() const (假设现在是 C++20)。这意味着 data_ 也将是 data() 中的 const ref,typename R 推断为其基础类型,然后我试图从传递给 Proxy ctor 的 const 引用构造 R& 范围。其他一切都破坏了 const 正确性。
  • @user1709708 我在答案中添加了 const 重载。是你要找的吗?
  • 或多或少是的,但是如果我使用上面的 Proxy 对象(至少不保留 const 正确性),我无法编译它。见这里ideone.com/XE9RUm
  • @user1709708 你需要使用Proxy&lt;const Type&gt;{data_} 来实现 const 重载。
  • 我明白了。我认为我的实际代码的问题在于我没有使用 auto 作为代理的开始/结束函数的返回类型,而是指定了迭代器类型。这又导致了 constness 问题,因为我没有让它依赖于 R 的 constness。无论如何,现在一切正常,非常感谢。
猜你喜欢
  • 2018-01-13
  • 2018-03-07
  • 2015-06-01
  • 2020-12-19
  • 2020-07-27
  • 2020-08-11
  • 2022-07-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多