I'm curious why the compiler doesn't complain if I hand over a const-pointer to a non-const-pointer as parameter in a constructor of a class that itself, which of course is at construction time not const.
#include <iostream>
class A
{
public:
A(int* v) : v(v) {}
int* v;
};
class B
{
public:
B() : o(23), v(&o) {}
const A cc() const
{
return A(v); //expected const to non-cosnt error here, but seems valid
}
int o;
int* v;
};
int main() {
B b;
const B* bc = &b;
A a = bc->cc();
*(a.v) = 25; // here I'm changing an originally const value
std::cout << b.o << std::endl;
return 0;
}
With defining cc() as const I expected the line where the return value is initialized an error message about converting a const to a non-const. The code-snippet compiles fine and in the end I get the output "25" even so that should have been const.
The A constructor expects a copy of a pointer instead of a reference. It is absolutely legal to copy a int * const into an int*. Therefore, no error occurs.
You should use references instead of pointers when you want a consistent behavior regarding constness. By the way, returning const objects is a bad idea.
You're confusing int const* (or const int*) and int* const.
Copying a const object into a non-const new object is fine, whether that object is a pointer or not. That's all you're doing here; the pointer itself is const (it's int* const), so you can pretty much do what you like with a copy of it.
What is not ok is copying a int const* into a int*, because now you're saying the thing being pointed to has magically lost its const-protection.
constness of an object does not propagate to constness of the pointee of a pointer member. The pointer becomes const but the pointee stays non-const. Even if the pointer is const, you can easily obtain a non-const pointer by simply copying the pointer which is exactly what happens as the pointer is passed by value.
Related
Why does returning the reference to a pointed-to member variable work, but not the other? I know that a const member function should only return const references, but why does that not seem true for pointers?
class MyClass
{
private:
int * a;
int b;
public:
MyClass() { a = new int; }
~MyClass() { delete a; }
int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad
};
int main(void)
{
MyClass m;
m.geta() = 5; //works????
m.getb() = 7; //doesn't compile
return 0;
}
int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad
In a const-function, every data member becomes const in such way that it cannot be modified. int becomes const int, int * becomes int * const, and so on.
Since the type of a in your first function becomes int * const, as opposed to const int *, so you can change the data (which is modifiable):
m.geta() = 5; //works, as the data is modifiable
Difference between : const int* and int * const.
const int* means the pointer is non-const, but the data the pointer points to is const.
int * const means the pointer is const, but the data the pointer points to is non-const.
Your second function tries to return const int &, since the type of b become const int. But you've mentioned the actual return type in your code as int &, so this function would not even compile (see this), irrespective of what you do in main(), because the return type doesn't match. Here is the fix:
const int & getb(void) const { return b; }
Now it compiles fine!.
Because a becomes int * const a;. That is, you cannot change the value of a (change what it points at), just as const says. The const-ness of what a points at is a completely different issue.
Please see my answer here for a thorough discussion of const and const member functions.
Nawaz's answer is very good. However, the point about the compiler catching the error need not always hold. The C++ FAQ warns about it here.
The good news is that the compiler will often catch you if you get this wrong. In particular, if you accidentally return a member of your this object by non-const reference [...] the compiler will often detect it and give you a compile-time error [...].
The bad news is that the compiler won’t always catch you: there are some cases where the compiler simply won’t ever give you a compile-time error message.
Translation: you need to think. If that scares you, find another line of work; “think” is not a four-letter word.
Would it be undefined behavior to change where a pointer points, when its data is const? Example:
const char* p = "foo";
p = "boo";
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
Extra question: and altering not const data of a const pointer? Would be UB? Example:
char* const p = "foo";
(*(char**)&p) = (char*)malloc(strlen(p));
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
This is correct. The pointer is not const so you can change it to point to something else if you want. In this case it won't cause a meory leak but remember that if the pointer points to data allocated with new and it is the only pointer to that data then you need to call delete before reassigning the pointer otherwise you'll have a meory leak.
Extra question: and removing the constness of pointer? Would be UB?
It is only UB if you try to modify a const object you removed const from, which you do in this case. Just removing const is okay, and sometimes needed, but you are never allowed to modify the object unless it was not const to begin with. For example the following is legal since foo is not const.
int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
on the other hand
const int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
is not legal as foo is const
The code in the first snippet is 100% correct. You have a pointer to const p, which you repoint to something else. All good and in mint condition.
The second piece of code is ill-formed. You can't modify an object after removing the constness, if original object was const-qualified (which string literal is).
Would it be undefined behavior to change where a pointer points, when its data is const? Example:
const char* p = "foo";
p = "boo";
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
Extra question: and altering not const data of a const pointer? Would be UB? Example:
char* const p = "foo";
(*(char**)&p) = (char*)malloc(strlen(p));
I believe that this is not UB, because the pointer itself is not const and I'm not modifying the "foo" object.
This is correct. The pointer is not const so you can change it to point to something else if you want. In this case it won't cause a meory leak but remember that if the pointer points to data allocated with new and it is the only pointer to that data then you need to call delete before reassigning the pointer otherwise you'll have a meory leak.
Extra question: and removing the constness of pointer? Would be UB?
It is only UB if you try to modify a const object you removed const from, which you do in this case. Just removing const is okay, and sometimes needed, but you are never allowed to modify the object unless it was not const to begin with. For example the following is legal since foo is not const.
int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
on the other hand
const int foo = 42;
void bar(int const& baz) { const_cast<int&>(baz) = 21; }
int main()
{
bar(foo);
}
is not legal as foo is const
The code in the first snippet is 100% correct. You have a pointer to const p, which you repoint to something else. All good and in mint condition.
The second piece of code is ill-formed. You can't modify an object after removing the constness, if original object was const-qualified (which string literal is).
I have two classes TestClass and OtherClass, where TestClass has a member variable of type OtherClass named m_otherClass. Note that this is not declared to be const.
In the minimal example provided below; when m_otherClass is a pointer, then everything compiles and runs fine. If I change this to be a non-pointer, then I get compiler errors (the changes are commented out in the minimal example):
"Non-const function 'setOtherMember' is called on const object"
error: passing 'const OtherClass' as 'this' argument discards qualifiers [-fpermissive] m_otherClass.setOtherMember();
#include <iostream>
#include <memory>
class OtherClass {
public:
void setOtherMember() {
m_otherMember = 2;
std::cout << "Other member is now 2" << std::endl;
}
private:
int m_otherMember = 0;
};
class TestClass {
public:
TestClass(): m_otherClass(std::make_unique<OtherClass>())
// TestClass()
{}
void myMethod() const {
m_otherClass->setOtherMember();
// m_otherClass.setOtherMember();
}
private:
std::unique_ptr<OtherClass> m_otherClass;
// OtherClass m_otherClass; // If changing to this I get the error!!
};
int main() {
TestClass testClass;
testClass.myMethod();
return 0;
}
Is this because myMethod() is const (and then promising not to change any member variables), whereas setOtherMember() is non-const and is changing OtherClass's member variable, and then indirectly also the m_otherClass object?
But why does this not fail then when m_otherClass is a pointer?
And why does the compiler error says that passing 'const OtherClass' as 'this'argument fails, when m_otherClass has not been declared to be const?
Const qualified member functions in most cases aren't allowed to change an object's members state. This means, that every member which is not mutable can not be modified in this function body. When dealing with pointers, you just say, that you won't modify a pointer value, not a pointee itself. It is because constness of a pointer is not propagated on its pointee.
In the upcoming standards it will be possible to change this behaviour by using propagate_const.
A simpler example to demonstrate the difference.
struct Foo { int m; };
const Foo f = {10};
f.m = 20; // Not allowed since modifying f.m modifies f.
struct Bar { int* p; };
int i = 10;
int j = 20;
const Bar b = {&i};
b.p = &j; // Not allowed since modifying b.p modifies b.
*(b.p) = j; // Allowed since it does not change b or b.p. It changes the value
// of what b.p points to.
When you put const on a method, all your data members are treated as being const. And that is why you get the error when you have OtherClass as a value, since it turns into a value of const OtherClass.
Now when you use a pointer of OtherClass you get const std::unique_ptr<OtherClass> and the const applies only to the pointer but not to the value it points to.
But why does this not fail then when m_otherClass is a pointer?
Because it is const correct to modify an object pointed by a const pointer, as long as it is a pointer to non-const such as in your case.
And why does the compiler error says that passing 'const OtherClass' as 'this'argument fails, when m_otherClass has not been declared to be const?
myMethod is declared const. Therefore this is a pointer to const. Therefore the lvalue this->m_otherClass is also const regardless of whether the object named by the lvalue is const or not.
#include <iostream>
using namespace std;
class Object
{
public:
Object() {}
void Print() const
{
cout << "const" << endl;
}
void Print()
{
cout << "mutable" << endl;
}
};
void print_obj(const Object& obj)
{
obj.Print();
}
int main()
{
Object obj1;
const Object obj2;
Object*const pobj1 = &obj1;
print_obj(obj1);
print_obj(obj2);
obj1.Print();
obj2.Print();
pobj1->Print();
return 0;
}
I know the output is
const
const
mutable
const
mutable
I guess for const object, it looks for the const function. Otherwise the mutable function. But why is the last one mutable?
But why is the last one mutable?
Object*const pobj1 = &obj1;
declares pobj1 to be a const pointer to a non-const object. The object is still mutable but the pointer is not.
After the variable is initialized, you will not be able to change it to point to another variable.
pobj1 = &obj2; // Not OK.
But you can still change the variable it points to and call non-const member functions of the class through the pointer.
*pobj1 = obj2; // OK
But why is the last one mutable?
This is because the declaration is Object* const pobj1. Since we are working with pointers there are 2 const possibilities.
const Object* const pobj1 OR Object const * const pobj1
^^1^^ ^^2^^ ^^1^^ ^^2^^
// 1 applies to the types (i.e., Object)
// 2, which is what you're using applies to the pointer, therefore you're using a non-const Object
This
Object*const pobj1 = &obj1;
declares pobj1 to be a const pointer to a non-const object. If you doubt that,
just change it to
Object*const pobj2 = &obj2;
This won't compile because it throws away a const qualifier of obj2.
BTW, the restriction is that for const object, it can ONLY use const functions. 'const' after the parenthesis means that 'this*' pointer will be treated as a pointer to a const object within the method.
For non-const objects, it can use both const and non-const methods, but it will chose to use non-const first (if both are defined).
I believe you meant "const Object*". "const" can be applied to the pointer and/or the object the pointer is pointing to. The twist is that "Object * const" is the same as "Object const*".