Деликатная проблема с квалификатором const, когда на объекты ссылаются другие объекты - PullRequest
1 голос
/ 03 июня 2019

Представьте, что у меня есть объект A, который ссылается на объект B через его адрес.Адрес B дается в конструкторе A. Некоторые методы A модифицируют B, другие - нет.

Вот очень простая иллюстрация:

class B {
public :
void f1() const;
void f2();
};

class A {
public :
 A(B * pb) {m_pb=pb;}
 void g1() {m_pb->f1();} // g1 do not modify *m_pb
 void g2() {m_pb->f2();} // g2 do modify *m_pb
private :
  B * m_pb;
}

Теперь представьте, что вВ какой-то части моего кода у меня есть переменная, тип которой const B &.Я хочу создать объект A для вызова метода g1, который не изменяет объект B. Но я вообще не могу построить объект A.

Одним из решений было бы создание 2 классов A1 и A2 с ссылками A1const B * и определяющий только метод g1, в то время как A2 будет ссылаться на B * и определит как g1, так и g2.(g1 будет определен в 2 разных местах).Я не нахожу это решение очень элегантным.

Интересно, есть ли способ объяснить компилятору, что: const A не изменит объект B, на который он ссылается. Так что объект const A можетбыть построенным с использованием объекта const B.

1 Ответ

2 голосов
/ 03 июня 2019

Интересно, есть ли способ объяснить компилятору, что: const A не изменит объект B, на который он ссылается. Таким образом, объект const A может быть создан с использованием объекта const B.

То, что вы хотите, это const constructor , а его нет. Но вы можете избежать дублирования интерфейса с наследованием:

struct B{
    void f1() const{}
    void f2() {}
};

class constA
{
public:
    constA(const B* b):b(b){}
    // Should be const as it does not changes `constA` object.
    void g1() const {b->f1();}

    // No harm in this, but can be protected if you want
    const B* getB() const { return b;}
private:
    const B* b;
};

class A : public constA
{
public:
    A(B* b):constA(b){}
    void g2() const { getMutableB()->f2();}
private:
    B* getMutableB() const { return const_cast<B*>(getB());}
};

int main()
{
   B b;
   const B cb;

   A a(&b);
   a.g2();
   a.g1();

   constA ca(&cb);
   ca.g1();
   //ca.g2();

   constA ca2(&b);
   ca.g1();
   //ca.g2();

}

РЕДАКТИРОВАТЬ: (По запросу от @ бывшие известныеas_463035818, с которым я согласен) Быстрое переподготовка на const_cast:

int main()
{
    const int x = 5;
    int y = 5;
    const int* c_ptr = &x; 
    //'ptr' is valid object with known value.
    int* ptr = const_cast<int*>(c_ptr);
    // Is valid because '*ptr' is not modified
    int value = *ptr;
    // Undefined behaviour because '*ptr' is const object.
    *ptr = 5;

    const int* c_ptr2 = &y; 
    //'ptr2' is valid object with known value.
    int* ptr2 = const_cast<int*>(c_ptr2);
    // Is valid because '*ptr2' is not modified
    int value = *ptr2;
    // Is valid because '*ptr2' is not a const object.
    *ptr2 = 5;
}

Учитывая, что A имеет только неконстантный B конструктор, тогда getB() всегда возвращает указатель на объект, который не является константным. Так что вторая часть приведенного выше примера применима и все безопасно.

Обратите внимание, что приведение констант const B для передачи его на A будет вызывать UB при вызове g2, но это верно для всех B* ptr с неизвестным происхождением, и класс A ничего не может с этим поделать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...