2

I have the following test code:

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

Where funcA does not have a constdefinition and funcB has a const definition.

When compiling the code, I get the error:

passing ‘const A’ as ‘this’ argument discards qualifiers [-fpermissive]

This should happen because I am calling inside a const function a non-const function.

However, when I do:

class B
: public A
{
    public:
    A* t;

    void funcB() const
    {
       t->funcA();
    }
};

The code compiles and works. Why is that? I am just compiling with g++ -g main.cpp, and I missing flags for this? or is this behavior to be expected?

Manuel Oliveira
  • 527
  • 5
  • 19
  • You want `const A *t` then instead of `A *t`. Remember, you're modifying the object `t` points to, not `t` itself, so the compiler thinks this is fine. – mediocrevegetable1 Oct 18 '21 at 14:49
  • 2
    Why do you have `A` both as a base class of `B` *and* as a member of `B`? You are not using the base class at all, so maybe remove it from the snippets (to make them less confusing) and keep just the member `t`. The first snippet won’t compile, because `t` (an actual instance of `A` embedded in an instance of `B`) is `const`, just like the surrounding `B` instance that `this` is pointing at. In the second snippet, `t` is pointing at a non-`const` instance of `A` (even though the pointer `t` itself is `const`), on which you can (of course) call arbitrary non-`const` methods. – Andrej Podzimek Oct 18 '21 at 14:51
  • 1
    It's `t` itself that is `const`, not the object pointed at by `t`. –  Oct 18 '21 at 14:52
  • 1
    https://www.learncpp.com/cpp-tutorial/pointers-and-const/ – sp2danny Oct 18 '21 at 14:57
  • [This question](https://stackoverflow.com/questions/1143262/what-is-the-difference-between-const-int-const-int-const-and-int-const) is a little broader than yours but it covers the basics. You can have a pointer to an object, a pointer to a `const` object, a `const` (pointer to object), and a `const` pointer to a `const` object. Inside `funcB`, `t` is a `const` pointer to a variable `A`. – Nathan Pierson Oct 18 '21 at 14:58
  • 1
    [std::experimental::propagate_const](https://en.cppreference.com/w/cpp/experimental/propagate_const) might have expected behavior. – Jarod42 Oct 18 '21 at 15:01

2 Answers2

3

You're not modifying the pointer member variable, you're modifying the value that it is pointing to, so it compiles.

The t variable is a pointer that points to a variable of type A. Doing t->funcA(); may change the value that the variable points to, but it does not change the pointer member itself, which is all the compiler is required to enforce here.

If you modified the actual pointer like this, this function would not compile.

void funcB() const 
{
    t = nullptr; //Will not compile
}

If you want to prevent someone from modifying the A variable through the pointer, simply declare the pointer as const. This will prevent modifying the value it points to.

const A* t;
Weston McNamara
  • 386
  • 5
  • 13
1

Case I: Explaination of your 1st code snippet

When we write

void funcA();  

this means funcA() has a implicit parameter whose type is A *const i.e., a const pointer to a non const A object. This in turn means we can call this function only on nonconst A objects. If you try to call this member function on a const A object you will get the mentioned error.

For showing my point lets try to call funcA on a const A object. The code is as follows:

#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
}

The above snippet gives the same error as you mentioned as can be seen here.

And this exactly what happens in your 1st code snippet. Now i am going to explain in steps what happens in your 1st code snippet:

Step 1. You have funcB(); declarared as const member function. Which means its own implicit parameter has type const B *const i.e., a const pointer to const B object. This in turn means the data members(including data member t) of B are themselves const.

Step 2. Now since t is const and as i said in my custom example, you can't call a nonconst member function on a const object. So you get your mentioned error.

Case 2: Explaination of your 2nd code snippet

In the second case you have changed the data member t to be of type A* that is a pointer to A object.

Now lets look at in steps what happens in this case:

Step 1. You have funcB(); declarared as const member function. Which means its own implicit parameter has type const B *const i.e., a const pointer to const B object. This in turn means the data members(including data member t) of B are themselves const. The difference this time is that t will become a const pointer to A i.e., t is of type A* const

Step 2. Again funcA has implicit parameter that is of type A *const since funcA is not a const member function. And this type exactly matches with what you pass using the call t->funcA(); . So you don't get any error in this case.

Jason
  • 36,170
  • 5
  • 26
  • 60