staxyz
staxyz

Reputation: 167

C++: Why can I pass a pointer-valued member variable from a const member function to an external function taking a non const pointer parameter?

I am using a function from an external library (here represented by the printer-function).
By chance, I noticed, that I can pass a class member from a const member function like done by the print()-function. Even if I compile using

g++ -Wall -Wextra -Wpedantic -Werror -o test test.cpp

I don't obtain an error.

This seems to be horribly wrong, because effectively I am modifying the object by calling a const member function. Why is the compiler not complaining? Here is the code:

#include <iostream>
using namespace std;

void printer(void* data) {
    static_cast<char*>(data)[0] = '1';
    cout << static_cast<char*>(data) << endl;
}

void setUp(char* data, int length) {
    for(int i=0; i<length; i++)
        data[i] = '0';
    data[length] = '\0';
}

class Test {
    private:
        char* data;

    public:
        Test(int length) {
            data = new char(length+1);
            setUp(this->data, length);
        }

        ~Test() {
            delete data;
        }

        void print() const {
            printer(this->data);
        }
};

int main() {
    Test T(3);
    T.print();
    return 0;
}

My best guess is, that the compiler only guarantees, that the pointer variable is not modified, i.e. not pointing to a different physical address after the function call. But I also noticed, that the compiler throws errors when I try something similar with a std::vector<char> instead of char* and a modified print-function:

void print() const {
    printer(this->myVector.data());
}

So if my guess is correct, why is it not possible to do something similar with the pointer obtained by data() of a STL vector?

Upvotes: 3

Views: 399

Answers (2)

Useless
Useless

Reputation: 67733

  1. You have a char* data member.

  2. In the context of a const-qualified method, that means the member may not be mutated.

  3. The member is the pointer. Changing a pointer means changing where it points.

  4. Just to be clear, the effective qualified type inside the method is

     char * const
    

    and not

     const char *
    
  5. Which is all to confirm that you're absolutely right that

    My best guess is, that the compiler only guarantees, that the pointer variable is not modified, i.e. not pointing to a different ... address after the function call.

However, for the vector, you want to call data on something that again has effective qualified type std::vector<char> const& ... so you need the const-qualified overload of std::vector::data, and that returns const char*. This is what you expected in the first place, but it's not the same behaviour as the raw pointer.

The reason for the difference is that the vector was written to express ownership of that data, so a const vector should have const contents. Raw pointers may own the data they point at, but there's no way to express this, and no way to have different const behaviour for owning and non-owning raw pointers. This is one of the may reasons to avoid raw pointers where possible.

Upvotes: 5

Den-Jason
Den-Jason

Reputation: 2573

This is a common way of allowing a const method to change data. The immutable thing in Test is the pointer address data; which is not altered by print. You are nonetheless free to mutate what the pointer is addressing.

Another way round a const method is to declare a member as being mutable. Arguably, ensuring an internal string attribute is e.g. null terminated for printing could be considered a legitimate use of mutable.

See this piece from Kate Gregory at CppCon 2017 where she provides an interesting example (a method is designed to be const but a cache is added later): https://youtu.be/XkDEzfpdcSg?t=854

Also see this question for a lively discussion on the topic: Does the 'mutable' keyword have any purpose other than allowing the variable to be modified by a const function?

Upvotes: 1

Related Questions