如何为 class 提供多个 begin/end 代理
How to provide multiple begin/end proxies for a class
鉴于 classes
struct Data
{
void bar() const;
void baz();
}
class Foo
{
std::vector<Data> data;
std::map<size_t, Data> indexed_data;
}
我想在 class 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
}
但是,我不想通过仅返回对容器的引用来公开 class 内部结构。我可能还会使用 classes,其中我想要迭代的范围未实现为容器。所以我基本上想创建某种代理对象,它只是 begin/end 对的包装器,这样我就可以在我的 class 中迭代多个东西。
此外,我希望它如上所示是 const 正确的。有什么众所周知的模式可以实现这一点吗?
考虑三种情况。
如果您想授予对内部数据的完全访问权限,只需为 return 创建一个函数:(简单地创建成员 public 也是一种选择)
class C {
public:
Type& data() { return data_; }
const Type& data() const { return data_; }
private:
Type data_;
};
如果您想要 read-only 访问您的内部数据,只需删除 non-const 重载:
class C {
public:
const Type& data() const { return data_; }
private:
Type data_;
};
如果您想授予 element-only 访问内部数据的权限,即您对每个单独的元素具有可变访问权限(当 C
本身是 non-const 时),但是您无法更改容器本身(例如,插入新元素),您需要 return 代理。从 C++20 开始,我们可以 return a 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;
};
那么你可以return Proxy{data_}
:
class C {
public:
auto data() { return Proxy{data_}; }
auto data() const { return Proxy{data_}; }
private:
Type data_;
};
在C++17之前,你可以这样写而不需要class模板参数推导:
class C {
public:
auto data() { return Proxy< Type>{data_}; }
auto data() const { return Proxy<const Type>{data_}; }
private:
Type data_;
};
鉴于 classes
struct Data
{
void bar() const;
void baz();
}
class Foo
{
std::vector<Data> data;
std::map<size_t, Data> indexed_data;
}
我想在 class 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
}
但是,我不想通过仅返回对容器的引用来公开 class 内部结构。我可能还会使用 classes,其中我想要迭代的范围未实现为容器。所以我基本上想创建某种代理对象,它只是 begin/end 对的包装器,这样我就可以在我的 class 中迭代多个东西。 此外,我希望它如上所示是 const 正确的。有什么众所周知的模式可以实现这一点吗?
考虑三种情况。
如果您想授予对内部数据的完全访问权限,只需为 return 创建一个函数:(简单地创建成员 public 也是一种选择)
class C {
public:
Type& data() { return data_; }
const Type& data() const { return data_; }
private:
Type data_;
};
如果您想要 read-only 访问您的内部数据,只需删除 non-const 重载:
class C {
public:
const Type& data() const { return data_; }
private:
Type data_;
};
如果您想授予 element-only 访问内部数据的权限,即您对每个单独的元素具有可变访问权限(当 C
本身是 non-const 时),但是您无法更改容器本身(例如,插入新元素),您需要 return 代理。从 C++20 开始,我们可以 return a 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;
};
那么你可以return Proxy{data_}
:
class C {
public:
auto data() { return Proxy{data_}; }
auto data() const { return Proxy{data_}; }
private:
Type data_;
};
在C++17之前,你可以这样写而不需要class模板参数推导:
class C {
public:
auto data() { return Proxy< Type>{data_}; }
auto data() const { return Proxy<const Type>{data_}; }
private:
Type data_;
};