const корректность для указателей

class InnerTest
{
public:
    InnerTest() {};
    ~InnerTest() {};

    void some_non_const_method() { std::cout << ++m_data << std::endl; }

private:
    int m_data{ 0 };
};

class Test
{
public:
    Test() {};
    ~Test() {};

    // error C2662: 'void InnerTest::some_non_const_method(void)': cannot convert 'this' pointer from 'const InnerTest' to 'InnerTest &'
    void foo() const { m_test.some_non_const_method(); };

    void bar() const { m_ptest->some_non_const_method(); };

private:
    InnerTest m_test;
    std::unique_ptr<InnerTest> m_ptest{ std::make_unique<InnerTest>() };
};

int main()
{
    Test t1;

    t1.foo();
    t1.bar();

    return 0;
}

Итак есть примерно такой код, как можно добиться const корректности при использовании указателей для константных методов?


Ответы (2 шт):

Автор решения: Harry

Компилятор прав - у вас член имеет тип

unique_ptr<InnerTest>

и его значение вы не меняете. Если вы хотите явно указать, что там у вас константный указатель - то скажите об этом прямо:

unique_ptr<const InnerTest>

Все, вообще-то корректно - const в функции-члене относится к объекту класса, так что m_test, входящий в объект класса, действительно не может вызывать неконстантный метод. Но какой-то левый, не пойми где находящийся объект InnerTest - с какой стати ему запрещать вызывать неконстантный метод?...

→ Ссылка
Автор решения: user7860670

В С++ const корректность не подразумевает транзитивности, т.е. const применяется только к самому объекту (указателю), но не распространяется на объекты, на которые тот указывает. А умные указатели из стандартной библиотеки просто мимикрируют поведением обычных указателей. Однако несложно реализовать обертку, имитирующую транзитивное поведение, для этого следует лишь добавить соотв. const перегрузки: online compiler

template< typename x_Value > class
t_TransitivePtr
{
    private: x_Value * m_p_value;

    public: explicit t_TransitivePtr(x_Value * p_value): m_p_value{p_value} {}

    public: x_Value & operator * (void) { return *m_p_value; }

    public: x_Value const & operator * (void) const { return *m_p_value; }

    public: x_Value * operator -> (void) { return m_p_value; }

    public: x_Value const * operator -> (void) const { return m_p_value; }
};

#include <string>

class
t_Test
{
    private: t_TransitivePtr<::std::string> m_ptr;

    public: explicit t_Test(::std::string & value): m_ptr{&value} {}

    public: void Method(void) { m_ptr->clear(); } // OK

    public: void ConstMethod(void) const { m_ptr->clear(); } // Error
};

В разных других языках может быть реализовано другое поведением. Например в языке D const полностью транзитивный; это подразумевает, что const указатель в принципе не может указывать на не const объект.

→ Ссылка