【问题标题】:Overloading operator=() with callback使用回调重载 operator=()
【发布时间】:2013-12-26 17:10:58
【问题描述】:

我有一个模板Property,它包装数据并提供其他服务。此模板专门用于基本数据(float、int_32、bool...),并且存在另一种用于基本类型向量的规范。比如:

template<typename T>
Property : public PropertyBase
{
public:
    // Base types specilization.
    void operator=(T const & data);
}

template<typename T>
Property<std::vector<T> > : public PropertyBase
{
public:
    // Specilization for vectors of base types.
    T const & operator[](std::size_t i) const;
}

关于基本数据的规范,我重载了operator=,以便在数据更改后调用回调。关于向量的特殊化,我重载了 operator[] 以获取对向量第 i 个元素的索引访问。

现在我想在基本类型上获得与 operator= 类似的行为,但我对向量的专长。我被困住了。我想做的是:

Property<std::vector<float> > myProp;
myProp[5] = 5.21f;

并期望在分配发生后调用回调。

问题是myProp[5]返回一个float,回调机制不是为float定义而是Property。

当然我可以写一个索引setter函数比如

template<typename T>
Property<std::vector<T> > : public PropertyBase
{
public:
    // Specilization for vectors of base types.
    T const & operator[](std::size_t i) const;

    void set(int i, T const & newValue);
}

但是使用 setter 的语法并不像我期望的那样干净。

我也可以在operator[] 中调用回调,但是回调是在分配之前调用的,而不是之后。

任何人都想获得这种简洁的语法,同时仍然能够在分配后调用我的回调?

谢谢!

PS:请注意,我的 Property 模板比所写的更复杂,并检查 T 是否为基本类型(此处未完成),但这与我的问题。

【问题讨论】:

  • 我相信您将不得不为 operator= 编写另一个重载,它将显式采用浮点类型。您如何区分 T 和 float (因为您的“数据”参数实际上可以是任何东西)?
  • 确保阅读More Effective C++的第30条

标签: c++ templates c++11 operator-overloading


【解决方案1】:

您需要从您的operator[]() 返回一个代理,该代理[隐式] 转换为您的类型T,并有一个赋值运算符拦截该赋值。这是一个草图:

template <typename T>
class proxy {
    Property<std::vector<T>>* property;
    std::size_t               index;
public:
    proxy(Property<std::vector<T>>* property, std::size_t index)
        : property(property)
        , index(index) {
    }
    operator T const&() const { return property->get(index); }
    T const& operator= (T const& other) {
        return this->property->set(index);
    }
};

然后您的operator[]() 将返回这样的代理:

 template <typename T>
 proxy<T> Property<std::vector<T>>::operator[](std::size_t index) {
     return proxy<T>(this, index);
 }

对字段读写的实际拦截将发生在对应的set()get()成员中。如果将proxy&lt;T&gt; 设为班级的friend,则这些可以是private。使用这样的代理类的主要问题是它不允许使用成员函数,例如:

Property<std::vector<std::string>> p;
p[0].c_str();

由于p[0] 恰好是proxy&lt;T&gt;,它没有.c_str() 函数。由于您声明您只支持某些基本类型,您可以支持代理上的合适操作。使用代理类型的另一个问题是它取出了一个转换,严格来说,返回代理而不是引用的迭代器不是一个合适的迭代器(尽管我似乎记得在 C+ 中放宽了迭代器要求+11)。

【讨论】:

  • 基本正确,但返回(非 const)T&amp; 会导致数据发生更改而不是通过赋值进行更改,这意味着错过了回调。
  • 可以通过const T* proxy::operator-&gt;() const { return &amp;get(); }const 成员访问直接启用const 成员访问(使用-&gt;)当然会在回调系统中创建新漏洞。
  • 我想知道operator=是否不应该return (*this)(类型为proxy&lt;T&gt;&amp;
  • @BenVoigt:我已经解决了返回T&amp; 而不是T const&amp; 的问题。基于问题成员访问不应该是一个问题。关于operator=() 的结果:我不确定。依稀记得以前讨论过代理分配的结果,两种类型各有优势。
  • @Dietmar & Ben:这是一个非常有趣的方法!我不确定我是否理解当你说“它取出一个转换,严格来说,返回代理而不是引用的迭代器不是正确的迭代器”。你能提供更多细节吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多