Reputation: 387
I read the question posted on Why does C++ not have a const constructor?
I am still confused why that program can compile. And I tried to offer my opinion on the question, I don't know why it was deleted. So I have to ask the question again.
Here is the program
class Cheater
{
public:
Cheater(int avalue) :
value(avalue),
cheaterPtr(this) //conceptually odd legality in const Cheater ctor
{}
Cheater& getCheaterPtr() const {return *cheaterPtr;}
int value;
private:
Cheater * cheaterPtr;
};
int main()
{
const Cheater cheater(7); //Initialize the value to 7
// cheater.value = 4; //good, illegal
cheater.getCheaterPtr().value = 4; //oops, legal
return 0;
}
And my confusion is :
const Cheater cheater(7)
creates a const object cheater, in its constructor
Cheater(int avalue) :
value(avalue),
cheaterPtr(this) //conceptually odd legality in const Cheater ctor
{}
'this' pointer was used to initialize cheaterPtr
.
I think it shouldn't be right. cheater
is a const object, whose this pointer should be something like: const Cheater* const this;
which means the pointer it self and the object the pointer points to should both const, we can neither change the value of the pointer or modify the object the pointer points to.
but object cheater
's cheaterPtr
member is something like Cheater* const cheaterPtr
. Which means the pointer is const but the object it points to can be nonconst.
As we know, pointer-to-const to pointer-to-nonconst conversion is not allowed:
int i = 0;
const int* ptrToConst = &i;
int * const constPtr = ptrToConst; // illegal. invalid conversion from 'const int*' to 'int*'
How can conversion from pointer-to-const to pointer-to-nonconst be allowed in the initializer list? What really happened?
And here is a discription about "constness" in constructors I tried to offer to the original post:
"Unlike other member functions, constructors may not be declared as const. When we create a const object of a class type, the object does not assume its 'constness' until after the constructor completes the object's initialization. Thus, constructors can write to const objects during their construction."
--C++ Primer (5th Edition) P262 7.1.4 Constructors
Upvotes: 2
Views: 896
Reputation: 66194
Your assumptions are incorrect. Taking them one at a time, first code annotation.
class Cheater
{
public:
Cheater(int avalue) :
value(avalue),
cheaterPtr(this) // NOTE: perfectly legal, as *this is non-const
// in the construction context.
{}
// NOTE: method is viable as const. it makes no modifications
// to any members, invokes no non-const member functions, and
// makes no attempt to pass *this as a non-const parameter. the
// code neither knows, nor cares whether `cheaterPtr`points to
// *this or not.
Cheater& getCheaterPtr() const {return *cheaterPtr;}
int value;
private:
Cheater * cheaterPtr; // NOTE: member pointer is non-const.
};
int main()
{
// NOTE: perfectly ok. we're creating a const Cheater object
// which means we cannot fire non-const members or pass it
// by reference or address as a non-const parameter to anything.
const Cheater cheater(7);
// NOTE: completely lega. Invoking a const-method on a const
// object. That it returns a non-const reference is irrelevant
// to the const-ness of the object and member function.
cheater.getCheaterPtr().value = 4;
return 0;
}
You said:
I think it shouldn't be right. cheater is a const object whose this pointer should be something like:
const Cheater* const this
cheater
is const
after construction. It must be non-const during construction. Further, the constructor does not (and cannot) know the caller has indicated the object will be const
. All it knows is its time to construct an object, so thats what it does. Furthermore, after construction &cheater
is const Cheater *
. Making the actual pointer var itself also const
is simply non-applicable in this context.
And then...
...object cheater's cheaterPtr member is something like
Cheater* const cheaterPtr;
This is actually an incredibly accurate way to describe this. Because cheater
is const
its members are as well, which means the cheaterPtr
member is const
; not what it points to. You cannot change the pointer value, but because it is not a pointer-to-const-object you can freely use that pointer to modify what it points to, which in this case happens to be this
.
If you wanted both the pointer and its pointed-to-object to be const you whould have declared it as const Cheater *cheaterPtr;
in the member list. (and doing that, btw, makes only that mutable action through the getCheaterPointer()
invalid. It would have to return a const Cheater*
as well, which means of course the assignment would fail.
In short, this code is entirely valid. What you're wanting to see (construction awareness of the callers const-ness) is not part of the language, and indeed it cannot be if you want your constructors to have the latitude to... well, construct.
Upvotes: 1
Reputation: 171117
If constructors were const
, they couldn't construct their object - they couldn't write into its data!
The code you cite as "legal:"
cheater.getCheaterPtr().value = 4; //oops, legal
is not actually legal. While it compiles, its behaviour is undefined, because it modifies a const
object through a non-const lvalue. It's exactly the same as this:
const int value = 0;
const int * p = &value;
*const_cast<int*>(p) = 4;
This will also compile, but it's still illegal (has UB).
Upvotes: 3