【问题标题】:Const and non-const access resolves to different overloads?常量和非常量访问解析为不同的重载?
【发布时间】:2010-10-13 08:44:50
【问题描述】:

让我说我们有一个简单的编程任务。但为了清楚起见,我从几个代码示例开始。 首先,我们编写了某种数据容器类,但出于任务的目的,无论该类是什么。我们只需要它的行为是正确的。

class DataComponent {
public:
    const std::string& getCaption() const {
        return caption;
    }

    void setCaption(const std::string& s) {
        caption = s;
    }

private:
    std::string caption;
};

然后让我们假设我们有一个泛型类,其行为类似于任意封装类实例的外观。假设我们重载了成员访问运算符 (->)。

template <typename T> class Component {
public:
    Component() { instance = new T(); }

    ...

    const T* operator-> () const {
        return instance;
    }

    T* operator-> () {
        // but there might be additional magic
        return instance;
    }

private:
    T *instance;
};

此时我应该说一下我希望它如何工作:

  • 如果我们通过成员访问运算符 (component-&gt;setCaption("foo")) 调用底层类的非常量成员函数,编译器会将非常量 T* operator-&gt; () 视为最佳选择。
  • 否则,如果我们试图以相同的方式调用底层类的 const 成员函数 (component-&gt;getCaption()),则编译器会选择 const T* operator-&gt; () const

上面的这个代码示例不会以这种方式工作,所以我很好奇是否有可能给编译器一个我提到的行为。任何提议。


编辑:让我们的成员访问操作符这样重载:

    const T* operator-> () const { return instance; }

    T* operator-> () {
        cout << "something going change" << endl;
        return instance;
    }

让我们在某处有一个变量Component&lt;DataComponent&gt; c。然后在调用c-&gt;getCaption() 时,stdout 应该保持沉默,但在调用c-&gt;setCaption("foo") 时,stdout 应该警告我们某些事情会发生变化。 VS 2010 编译器使标准输出在每次调用时都向我们发出警告。

我理解这样的语义假设c 同时表现为 const 和 non-const。但好奇仍在我的脑海中。

【问题讨论】:

    标签: c++ design-patterns templates


    【解决方案1】:

    调用 const 或非 const 成员完全取决于调用它的对象的 const 性,而不是某些后续操作。该决定是在考虑您在DataComponent 中调用的特定方法之前做出的。你仍然可以不那么直接地使用 DataComponent 周围的代理对象来破解所需的功能,const 和非const 转发getCaption()s。

    编辑:按要求提供详细信息(在我脑海中)。您需要转发声明其中的一些内容 - 我没有打扰,因为它使它更加混乱。如有任何疑虑/反馈,请务必加入。请注意,这基本上假设您由于某种原因不能/不想修改 Component,但它不是一个通用的模板化解决方案,可以简单地包裹任何任意类型 - 它的耦合度非常高并且维护负担很高。

    // know they can't call a non-const operation on T, so this is ok...
    const T* Component::operator->() const { return instance; }
    
    // they might invoke a non-const operation on T, so...
    DataComponent::Proxy Component::operator->() { return DataComponent.getProxy(*this); }
    

    class DataComponent:

    struct Proxy
    {
        Component& c_;
        DataComponent& d_;
    
        Proxy(Component& c, DataComponent& d) : c_(c), d_(d) { }
    
        const std::string& get_caption() const { return d_.get_caption(); }
    
        void set_caption(const std::string& s)
        {
            c_.on_pre_mutator(d_);
            d_.set_caption(s);
            c_.on_post_mutator(d_);
        }
    };
    

    然后

    DataComponent::Proxy DataComponent::getProxy(Component& c) { return Proxy(c, *this); }
    

    因此,这意味着您必须在某个地方手动编码转发功能。这很痛苦,但如果您这样做是为了调试或测试,这并非不合理。如果您这样做是为了添加锁或其他东西,那么可能有更好的选择。

    【讨论】:

    • 明白你关于 constness 的观点,谢谢,我记住了同样的观点。但是代理呢?我将不胜感激这种方法的任何详细描述。
    猜你喜欢
    • 2013-10-14
    • 2022-10-20
    • 1970-01-01
    • 2012-11-27
    • 1970-01-01
    • 2019-02-25
    • 1970-01-01
    • 2013-11-23
    • 1970-01-01
    相关资源
    最近更新 更多