用于访问容器<T>数据成员的正确 API

Proper API for access data members of container<T>

本文关键字:数据成员 API gt 访问 lt 用于      更新时间:2023-10-16

我有以下类:

class Document
{
public:
Document():
// default values for members, 
// ...
m_dirty{false}{}
// Accessor functions
template<class OutputStream>
Document& save(OutputStream stream)
{
// Write stuff to `stream`
// ...
m_dirty = false;
return *this;
}
bool dirty() const { return m_dirty; }
private:
Size2d m_canvas_size;
LayerStack m_layers;
LayerIndex m_current_layer;
std::vector<Palette> m_palettes;
PaletteIndex m_current_palette;
ColorIndex m_current_color;
std::vector<std::string> m_palette_names;
std::vector<std::string> m_layer_names;
bool m_dirty;
};

类是否应该具有用于直接修改m_palette元素的公共成员函数,如

Document& color(PaletteIndex, ColorIndex, Color)

,还是更";"正确";,只允许通过一对API的访问整个向量

std::vector<Palette> const& palettes();
Document& palettes(std::vector<Palette>&&);

第一种选择会更高效,因为它不需要创建数据成员的临时副本,但这种设计的一致使用会使接口变得臃肿。这将需要";深";类中每个容器的getter和setter。

注意脏标志。因此,以下内容将打破抽象:

std::vector<Palette>& palettes();

您可能有代理"传播";调色板修改中的脏标志,类似于:

template <typename T>
class DirtyProxy
{
T& data;
bool& dirty;
public:
DirtyProxy(T& data, bool& dirty) : data(data), dirty(dirty) {}
~DirtyProxy() { dirty = true;}
DirtyProxy(const DirtyProxy&) = delete;
T* operator ->() { return data; }
};

然后

DirtyProxy<Palette> palette(std::size_t i) { return {m_palettes.at(i), dirty}; }

我认为解决它最稳健的方法是使用回调。代理的一个问题是,它无法处理客户端代码抛出异常的情况(假设有强大的异常保证(。测试用例:

try
{
auto property_proxy = obj.getProperty();
// an exception is thrown here...
property_proxy->val = x;  // Never updated
}
catch(...)
{}
assert(!obj.dirty());

将失败,因为dtor总是设置脏标志。然而,使用回调

class Foo
{
public:
template<class F>
Foo& modifyInSitu(F&& f)
{
f(x);
m_dirty = true;
return *this
}
};

将仅在f(x)未抛出时更新m_dirty