如何强制指针数据成员的const-correctness

时间:2018-06-08 16:06:47

标签: c++ const const-correctness

在工作讨论之后,我们似乎无法强制执行"逻辑"具有指针数据成员的类的const-correctness,如下所示:

class Widget {
public:
    void Foo();
    void FooConst() const;
};

class WidgetManager {
public:
    WidgetManager() : _pW(std::shared_ptr<Widget>(new Widget())) { }

    void ManagerFoo()
    {
        _pW->Foo();         // should be OK, will not compile if declared as "const Widget*"
        _pW->FooConst();    // should be OK
    }

    void ManagerFooConst() const
    {
        _pW->Foo();         // should NOT be OK, will not compile if declared as "const Widget*"
        _pW->FooConst();    // should be OK
    }

    void RegenerateWidget()
    {
        _pW = std::shared_ptr<Widget>(new Widget());
    }

private:
    std::shared_ptr<Widget> _pW;
};

可以看出,我们希望WidgetManager::ManagerFooConst()无法调用WidgetManager指针成员的非const函数,同时仍允许从WidgetManager指针成员调用它们std::shared_ptr<const Widget>的其他非const函数。这意味着,将指针声明为const Widget*(即Widget)已经结束。

此外,我们希望在经理的整个生命周期内让指针引用另一个WidgetManager的选项,因此我们并不想将其作为数据成员保留(并且不能通过引用来保存它。)

当然,所有&#34;按位&#34;这里强制执行const-correctness,因为Widget的数据成员不能在const方法中修改(包括_pW指向的特定this),但我们希望实现& #34;逻辑&#34; const-correctness甚至是尖锐的成员都无法修改。

我们唯一想到的是添加Widget&#34;的const和非const&#34; getter;到class Widget { public: void Foo(); void FooConst() const; Widget* GetPtr() { return this; } const Widget* GetConstPtr() const { return this; } };

void WidgetManager::ManagerFoo()
{
    // shouldn't use "->" directly (lint?)

    _pW->GetPtr()->Foo();
    _pW->GetPtr()->FooConst();
    //_pW->GetConstPtr()->Foo();        // this won't compile (good)
    _pW->GetConstPtr()->FooConst();

}

void WidgetManager::ManagerFooConst() const
{
    // shouldn't use "->" directly (lint?)

    _pW->GetPtr()->Foo();               // shouldn't be used (lint?)
    _pW->GetPtr()->FooConst();          // shouldn't be used (lint?)
    //_pW->GetConstPtr()->Foo();        // this won't compile (good)
    _pW->GetConstPtr()->FooConst();
}

直接使用这些代替箭头操作符:

operator->

但这太丑了,编译器肯定无法强制执行。

具体来说,尝试重置Widget*const Widget*的{​​{1}}似乎无法改变任何内容:ManagerFooConst()仍然可以调用_pW->Foo()

有没有办法实现这个目标?

4 个答案:

答案 0 :(得分:2)

您可以使用(或重新实施)std::experimental::propagate_const

然后你的代码将是:

class Widget {
public:
    void Foo();
    void FooConst() const;
};

class WidgetManager {
public:
    WidgetManager() : _pW(std::make_shared<Widget>()) {}

    void ManagerFoo()
    {
        _pW->Foo();      // OK
        _pW->FooConst(); // OK
    }

    void ManagerFooConst() const
    {
        _pW->Foo();         // not compile
        _pW->FooConst();    // OK
    }

private:
    std::experimental::propagate_const<std::shared_ptr<Widget>> _pW;
};

Demo

答案 1 :(得分:1)

考虑通过成员函数访问authenticate,该成员函数将AppComponent的常量反映到指向对象上。

authenticate

您将在此处收到一个错误。

shared_ptr

答案 2 :(得分:0)

一个简单的解决方案是使const和非const管理器有两种不同的类型:

template<class TWidget>
class WidgetManager {
    // ...
private:
    std::shared_ptr<TWidget> _pW;
};

WidgetManager<const Widget> const_wm;
const_wm.ManagerFoo(); // fail: _pW->Foo(): call non-const function of *_pw: a const Widget.

答案 3 :(得分:0)

除了这里提出的内容之外,你还可以引入一个概念&#34;拥有指针&#34;基于共享指针:

template<class T>
class owning_ptr {
    public:
    owning_ptr(T* data) : mData{data} {}
    // Put as many constructors as you need

    T* operator->() { return mData.get(); }
    const T* operator->() const { return mData.get(); }

private:
    std::shared_ptr<T> mData;    
};

此类具有与共享指针相同的功能,但具有不同的拥有数据的概念。

您可以像使用共享指针一样使用它:

class WidgetManager {

    ...

private:
    owning_ptr<Widget> _pW;
}