代码之家  ›  专栏  ›  技术社区  ›  Keynslug

const和non-const访问解析为不同的重载?

  •  0
  • Keynslug  · 技术社区  · 14 年前

    假设我们有一个简单的编程任务。但是为了清晰起见,我从很少的代码示例开始。 首先,我们编写了一个数据容器类,但是为了完成任务,不管这个类是什么。我们只需要它表现得正确。

    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->setCaption("foo") )编译器处理非常量 T* operator-> () 作为最佳选择。
    • 否则,如果我们试图以相同的方式调用基础类的const成员函数( component->getCaption() )编译器选择 const T* operator-> () const 另一方面。

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


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

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

    让我们有一个变量 Component<DataComponent> c 某处。然后打电话给 c->getCaption() stdout应该保持沉默,但在调用 c->setCaption("foo") stdout应该警告我们一些事情将会改变。 Vs2010编译器让stdout在这些调用中警告我们。

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

    1 回复  |  直到 14 年前
        1
  •  3
  •   Tony Delroy    14 年前

    是否调用const或non-const成员完全由调用它的对象的constance决定,而不是由某些后续操作决定。这个决定是在考虑您调用的特定方法之前做出的。 DataComponent . 您仍然可以使用围绕数据组件的代理对象来更不直接地破坏所需的功能,这两者都有 const 康斯特 转发 getCaption() S.

    编辑:根据要求的细节(在我的头上)。你需要提前申报一些东西——我没费心,因为这会让你更加困惑。处理任何问题/反馈。请注意,这基本上假定您出于某种原因不能/不想修改组件,但它不是一个通用的模板化解决方案,它可以简单地包装在任意类型上—它是非常重耦合的,并且具有很高的维护负担。

    // 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); }
    

    所以,这意味着你必须在某个地方传递代码转发功能。这是一种痛苦,但如果您这样做是为了调试或测试,这并不不合理。如果您这样做是为了添加一个锁或其他东西,那么可能有更好的选择。