Reputation: 167
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
Reputation: 67733
You have a char* data member.
In the context of a const-qualified method, that means the member may not be mutated.
The member is the pointer. Changing a pointer means changing where it points.
Just to be clear, the effective qualified type inside the method is
char * const
and not
const char *
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
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