#c #c 11
Вопрос:
У меня есть следующий тестовый код:
class A
{
public:
void funcA()
{
std::cout << "banana" << std::endl;
}
};
class B
: public A
{
public:
A t;
void funcB() const
{
t.funcA();
}
};
int main(int argCount, char *args[])
{
B tst;
tst.funcB();
}
Где funcA
нет const
определения и funcB
есть const
определение.
При компиляции кода я получаю ошибку:
passing ‘const A’ as ‘this’ argument discards qualifiers [-fpermissive]
Это должно произойти, потому что я вызываю внутри const
функции non-const
функцию.
Однако, когда я это сделаю:
class B
: public A
{
public:
A* t;
void funcB() const
{
t->funcA();
}
};
Код компилируется и работает. Это почему? Я просто компилируюсь с g -g main.cpp
, и мне не хватает флагов для этого? или такого поведения следует ожидать?
Комментарии:
1. Вы хотите
const A *t
тогда вместоA *t
этого . Помните, что вы изменяете объектt
, на который указывает объект, а неt
на него самого, поэтому компилятор считает, что это нормально.2.Почему у вас есть
A
и как базовый класс,B
и как членB
? Вы вообще не используете базовый класс, поэтому, возможно, удалите его из фрагментов (чтобы сделать их менее запутанными) и сохраните только элементt
. Первый фрагмент не будет компилироваться, потомуt
что (фактический экземплярA
встроен в экземплярB
)const
, как и окружающийB
экземпляр, на которыйthis
указывает. Во втором фрагменте,t
указывает на неconst
экземплярA
(хотя сам указательt
естьconst
), на котором вы можете (конечно) вызывать произвольные не —const
методы.3. Это
t
самоconst
по себе , а не объект, на который указываетt
.4. learncpp.com/cpp-tutorial/pointers-and-const
5. std::экспериментальный::propagate_const может иметь ожидаемое поведение.
Ответ №1:
Вы не изменяете переменную-член указателя, вы изменяете значение, на которое она указывает, поэтому она компилируется.
t
Переменная — это указатель, указывающий на переменную типа A
. Выполнение t->funcA();
может изменить значение, на которое указывает переменная, но оно не изменяет сам элемент указателя, и это все, что требуется компилятору для применения здесь.
Если бы вы изменили фактический указатель таким образом, эта функция не компилировалась бы.
void funcB() const
{
t = nullptr; //Will not compile
}
Если вы хотите запретить кому-либо изменять переменную A с помощью указателя, просто объявите указатель как const. Это предотвратит изменение значения, на которое оно указывает.
const A* t;
Ответ №2:
Случай I: Объяснение вашего 1-го фрагмента кода
Когда мы пишем
void funcA();
это означает funcA()
, что имеет неявный параметр, типом которого является A *const
, т. Е. Указатель const на неконстантный объект. Это, в свою очередь, означает, что мы можем вызывать эту функцию только для неконстантных объектов. Если вы попытаетесь вызвать эту функцию-член для объекта const A, вы получите указанную ошибку.
Чтобы показать мою точку зрения, давайте попробуем вызвать funcA
объект const A. Код выглядит следующим образом:
#include <iostream>
class A
{
public:
void funcA()
{
std::cout << "banana" << std::endl;
}
};
int main()
{
A nonconstobject;
nonconstobject.funcA(); //this works
const A constobject; //note the const here
constobject.funcA(); //this gives error: passing ‘const A’ as ‘this’ argument discards qualifiers
}
Приведенный выше фрагмент кода выдает ту же ошибку, о которой вы упомянули, как видно здесь.
И это именно то, что происходит в вашем 1-м фрагменте кода. Теперь я собираюсь пошагово объяснить, что происходит в вашем 1-м фрагменте кода:
Шаг 1. Вы funcB();
объявили как функцию-член const. Это означает, что его собственный неявный параметр имеет тип const B *const
, т. Е. Указатель const на объект const B. Это, в свою очередь, означает, что элементы данных(включая элемент данных t) B сами являются постоянными.
Шаг 2. Теперь, поскольку t
это const
и, как я сказал в своем пользовательском примере, вы не можете вызывать функцию-член nonconst для объекта const. Таким образом, вы получаете упомянутую вами ошибку.
Случай 2: Объяснение вашего 2-го фрагмента кода
Во втором случае вы изменили элемент данных t
на тип A*
, который является указателем на объект.
Теперь давайте рассмотрим пошагово, что происходит в этом случае:
Шаг 1. Вы funcB();
объявили как функцию-член const. Это означает, что его собственный неявный параметр имеет тип const B *const
, т. Е. Указатель const на объект const B. Это, в свою очередь, означает, что элементы данных(включая элемент данных t) B сами являются постоянными. Разница на этот раз в том, что t
станет постоянным указателем на A, т. е. t
имеет тип A* const
Шаг 2. Снова funcA
имеет неявный параметр, который имеет тип A *const
, так funcA
как не является функцией-членом const. И этот тип точно соответствует тому, что вы передаете с помощью вызова t->funcA();
. Так что в этом случае вы не получите никакой ошибки.